UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

152 lines (113 loc) 4.67 kB
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; }