@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
152 lines (113 loc) • 4.67 kB
JavaScript
import { Matrix4, NormalBlending, ShaderMaterial, Vector2, Vector4 } from "three";
import { FULL_SCREEN_QUAD_VERTEX_SHADER } from "../../../graphics/FULL_SCREEN_QUAD_VERTEX_SHADER.js";
/**
Shader for drawing fog of war as a screen-space post-process effect
@returns {ShaderMaterial}
*/
export function buildScreenSpaceFogOfWarShader() {
function vertexShader() {
return FULL_SCREEN_QUAD_VERTEX_SHADER;
}
function fragmentShader() {
return `
#include <packing>
uniform sampler2D tDepth;
uniform sampler2D tFog;
uniform vec4 uFogUvTransform;
uniform mat4 uProjectionInverse;
uniform mat4 uViewInverse;
uniform vec2 uResolution;
uniform vec4 uColor;
varying vec2 vUv;
const float c_samplesX = 5.0;
const float c_samplesY = 5.0;
const float c_halfSamplesX = c_samplesX / 2.0;
const float c_halfSamplesY = c_samplesY / 2.0;
const vec2 sigma = vec2(23.0, 23.0);
const vec2 sigma2_sq = sigma*sigma*2.0;
const float radius = 0.9;
const vec2 samplingBias = vec2(c_halfSamplesX,c_halfSamplesY)/ vec2( radius );
vec3 computeWorldPosition(){
// Convert screen coordinates to normalized device coordinates (NDC)
float normalizedDepth = texture2D( tDepth, vUv).x ;
vec4 ndc = vec4(
(vUv.x - 0.5) * 2.0,
(vUv.y - 0.5) * 2.0,
(normalizedDepth - 0.5) * 2.0,
1.0);
vec4 clip = uProjectionInverse * ndc;
vec3 result = (uViewInverse* (clip / clip.w) ).xyz;
return result;
}
float sampleFog(const in vec2 uv){
return texture2D( tFog, uv ).r;
}
float Gaussian(float sigma2_sq, float x){
return exp(-(x*x) / sigma2_sq);
}
float getFog(const in vec2 uv){
vec2 texelSize = samplingBias * uResolution;
float total = 0.0;
float ret = 0.0;
for (float iy = 0.0; iy < c_samplesY; ++iy){
float fy = Gaussian(sigma2_sq.y, iy - c_halfSamplesY);
float offsety = (iy-c_halfSamplesY) / texelSize.y;
for (float ix = 0.0; ix < c_samplesX; ++ix){
float fx = Gaussian(sigma2_sq.x, ix - c_halfSamplesX);
float offsetx = (ix-c_halfSamplesX) / texelSize.x;
float weight = fx * fy;
total += weight;
ret += texture2D(tFog, uv + vec2(offsetx, offsety)).x * weight;
}
}
return ret / total;
}
void main(){
//get world fragment position
vec3 worldPosition = computeWorldPosition();
vec2 fogUv = worldPosition.xz * uFogUvTransform.zw + uFogUvTransform.xy;
float fogValue = getFog( fogUv );
gl_FragColor = uColor;
gl_FragColor.a *= fogValue;
}
`;
}
const uniforms = {
tFog: {
type: 't',
/**
* @type {Texture}
*/
value: null
},
tDepth: {
type: 't',
/**
* @type {Texture}
*/
value: null
},
uViewInverse: { type: 'm4', value: new Matrix4() },
uProjectionInverse: { type: 'm4', value: new Matrix4() },
uColor: { type: 'c', value: new Vector4(0, 0, 0, 1) },
uFogUvTransform: { type: 'v4', value: new Vector4(0, 0, 1, 1) },
/**
* Fog texture resolution
*/
uResolution: { type: 'v2', value: new Vector2(0, 0) }
};
const material = new ShaderMaterial({
uniforms,
vertexShader: vertexShader(),
fragmentShader: fragmentShader(),
blending: NormalBlending,
lights: false,
fog: false,
depthTest: false,
depthWrite: false,
transparent: true,
vertexColors: false,
extensions: {}
});
return material;
}