UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

129 lines (90 loc) 4.22 kB
import { Vector2 } from "three"; const AmbientOcclusionShader = function () { return { uniforms: { "normalMap": { type: "t", value: null }, "heightMap": { type: "t", value: null }, "world_size": { type: "v2", value: new Vector2(512, 512) }, "rayLength": { type: 'f', value: 17 } }, defines: { 'NUM_SAMPLES': 64, 'NUM_RINGS': 7, }, vertexShader: [ "varying vec2 vUv;", "void main() {", "vUv = uv;", "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", "}" ].join("\n"), fragmentShader: [ ` uniform sampler2D normalMap; uniform sampler2D heightMap; uniform float rayLength; uniform vec2 world_size; varying vec2 vUv; vec3 get(float x, float y){ vec2 _uv = vUv.xy + vec2(x,y) / world_size; float h = texture2D(heightMap, _uv).x; return vec3( _uv.x * world_size.x, h, _uv.y * world_size.y ); } float hash1( float n ) { return fract( n*17.0*fract( n*0.3183099 ) ); } float hash1( vec2 p ) { p = 50.0*fract( p*0.3183099 ); return fract( p.x*p.y*(p.x+p.y) ); } // Non-sin based hash function, fast and has good randomness float hash12(vec2 p){ vec3 p3 = fract(vec3(p.xyx) * .1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } const float bias = 0.001; float pow2(float x){ return x*x; } float getOcclusion(vec3 origin, vec3 normal, vec3 hit_position){ vec3 viewDelta = hit_position - origin; float viewDistance = length( viewDelta ); float vn = dot( normal, viewDelta ); float a2 = (vn) / viewDistance - bias; float a1 = (1.0 + pow2( viewDistance ) ); return max(0.0, a2) / a1; } const float PI2 = 6.28318530717958; const float kernelRadius = 100.0; const float ANGLE_STEP = PI2 * float( NUM_RINGS ) / float( NUM_SAMPLES ); const float INV_NUM_SAMPLES = 1.0 / float( NUM_SAMPLES ); float getAmbientOcclusion(vec3 world_position, vec3 world_normal){ // jsfiddle that shows sample pattern: https://jsfiddle.net/a16ff1p7/ float angle = hash12( vUv ) * PI2; vec2 radius = vec2( rayLength * INV_NUM_SAMPLES ); float occlusionSum = 0.0; float weightSum = 0.0; for( int i = 0; i < NUM_SAMPLES; i ++ ) { vec2 sampleUv = vec2( cos( angle ), sin( angle ) ) * radius * float(i+1); angle += ANGLE_STEP; vec3 sample_pos = get(sampleUv.x, sampleUv.y); occlusionSum += getOcclusion(world_position, world_normal, sample_pos); weightSum += 1.0; } return occlusionSum/weightSum; } void main() { vec3 pos = get(0.0, 0.0); vec3 normal = texture2D( normalMap, vUv ).xzy; float occlusion = getAmbientOcclusion(pos, normal); float incident = 1.0 - occlusion; gl_FragColor = vec4(pow(incident,10.0), 0.0, 0.0, 1.0); } ` ].join('\n') } }; export default AmbientOcclusionShader;