diff --git a/assets/shaders/water.fs b/assets/shaders/water.fs index d692537..27596e5 100644 --- a/assets/shaders/water.fs +++ b/assets/shaders/water.fs @@ -9,37 +9,38 @@ uniform sampler2D normalMap; uniform sampler2D depthMap; uniform float uReflectivity; -uniform vec4 uTint; uniform float uDUDVOffset; uniform vec3 uLightColor; +uniform vec4 uTint; + +uniform float uShine; +uniform float uShineDamper; +uniform float uDistortionStrength; + +uniform float uNearPlane; +uniform float uFarPlane; varying vec2 uv; varying vec3 toCameraVector; varying vec3 fromLightVector; -const float waterDistortionStrength = 0.02; -const float shineDamper = 20.0; -const float reflectivity = 0.6; - -const float near = 0.1; -const float far = 10000.0; - void main() { vec2 ndc = (clipSpace.xy / clipSpace.w) / 2.0 + 0.5; vec2 refractTexCoords = vec2(ndc.x, ndc.y); vec2 reflectTexCoords = vec2(ndc.x, -ndc.y); float depth = 2.0 * texture2D(depthMap, refractTexCoords).r - 1.0; - float floorDistance = 2.0 * near * far / (far + near - depth * (far - near)); + float floorDistance = 2.0 * uNearPlane * uFarPlane / (uFarPlane + uNearPlane - depth * (uFarPlane - uNearPlane)); depth = 2.0 * gl_FragCoord.z - 1.0; - float waterDistance = 2.0 * near * far / (far + near - depth * (far - near)); + float waterDistance = 2.0 * uNearPlane * uFarPlane / (uFarPlane + uNearPlane - depth * (uFarPlane - uNearPlane)); float waterDepth = floorDistance - waterDistance; vec2 distortedTexCoords = texture2D(dudvMap, vec2(uv.x + uDUDVOffset, uv.y)).rg * 0.1; distortedTexCoords = uv + vec2(distortedTexCoords.x, distortedTexCoords.y + uDUDVOffset); // Between -1 and 1 - vec2 totalDistortion = (texture2D(dudvMap, distortedTexCoords).rg * 2.0 - 1.0) * waterDistortionStrength; + float depthDistortionBlend = clamp(waterDepth / 20.0, 0.0, 1.0); + vec2 totalDistortion = (texture2D(dudvMap, distortedTexCoords).rg * 2.0 - 1.0) * uDistortionStrength * depthDistortionBlend; refractTexCoords += totalDistortion; reflectTexCoords += totalDistortion; @@ -61,11 +62,14 @@ void main() { vec3 normal = vec3(normalMapColor.r * 2.0 - 1.0, normalMapColor.b, normalMapColor.g * 2.0 - 1.0); normal = normalize(normal); + float depthAlpha = clamp(waterDepth / 5.0, 0.0, 1.0); + vec3 reflectedLight = reflect(normalize(fromLightVector), normal); float specular = max(dot(reflectedLight, viewVector), 0.0); - specular = pow(specular, shineDamper); - vec3 specularHighlights = uLightColor * specular * reflectivity; + specular = pow(specular, uShineDamper); + vec3 specularHighlights = uLightColor * specular * uShine * depthAlpha; gl_FragColor = mix(reflectColor, refractColor, refractiveFactor); gl_FragColor = mix(gl_FragColor, vec4(uTint.xyz, 1.0), uTint.w) + vec4(specularHighlights, 0.0); + gl_FragColor.a = depthAlpha; } diff --git a/assets/textures/dudv.png b/assets/textures/dudv.png index 88e313f..82cdf7d 100644 Binary files a/assets/textures/dudv.png and b/assets/textures/dudv.png differ diff --git a/src/engine/components/water/index.js b/src/engine/components/water/index.js index 6bc090e..b6537f1 100644 --- a/src/engine/components/water/index.js +++ b/src/engine/components/water/index.js @@ -37,7 +37,7 @@ class WaterFBOs { // create the framebuffer gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer) // indicate that we will always render to color attachment 0 - gl.drawBuffers.drawBuffersWEBGL([gl.COLOR_ATTACHMENT0]) + gl.drawBuffers.drawBuffersWEBGL([gl.drawBuffers.COLOR_ATTACHMENT0_WEBGL]) return frameBuffer } @@ -80,6 +80,8 @@ class WaterFBOs { gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST) + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE) + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE) gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, texture, 0) return texture } @@ -94,12 +96,17 @@ class WaterFBOs { } class WaterTile extends Node { - constructor (pos, scale, reflectivity = 1, tint = [0.0, 0.3, 0.5, 0.2]) { + constructor (pos, scale, reflectivity = 0.1, tint = [0.0, 0.3, 0.5, 0.2]) { super(pos, [scale, 0.0, scale]) this.fbos = new WaterFBOs() + this.reflectivity = reflectivity this.tint = tint this.phase = 1.0 + + this.shininess = 0.7 + this.shineDamper = 20.0 + this.distortionStrength = 0.02 } initialize (gl) { @@ -107,7 +114,7 @@ class WaterTile extends Node { this.fbos.initialize(gl) } - async useDUDV (gl, file) { + async useDUDVMap (gl, file) { this.dudvMap = await Texture.createTexture2D(gl, await Resource.loadImage(file + '.png'), false, gl.LINEAR) } @@ -136,15 +143,25 @@ class WaterTile extends Node { prepare (gl, shader, cam, sun) { const transformLoc = shader.getUniformLocation(gl, 'uModelMatrix') + const camPosLoc = shader.getUniformLocation(gl, 'uCameraPosition') const refrLoc = shader.getUniformLocation(gl, 'refractionTexture') const reflLoc = shader.getUniformLocation(gl, 'reflectionTexture') - const depthLoc = shader.getUniformLocation(gl, 'depthTexture') + const depthLoc = shader.getUniformLocation(gl, 'depthMap') const dudvLoc = shader.getUniformLocation(gl, 'dudvMap') - const normalLoc = shader.getUniformLocation(gl, 'dudvMap') + const normalLoc = shader.getUniformLocation(gl, 'normalMap') + const reflectivityLoc = shader.getUniformLocation(gl, 'uReflectivity') const tintLoc = shader.getUniformLocation(gl, 'uTint') const phaseLoc = shader.getUniformLocation(gl, 'uDUDVOffset') + + const shininessLoc = shader.getUniformLocation(gl, 'uShine') + const damperLoc = shader.getUniformLocation(gl, 'uShineDamper') + const distortLoc = shader.getUniformLocation(gl, 'uDistortionStrength') + + const farLoc = shader.getUniformLocation(gl, 'uFarPlane') + const nearLoc = shader.getUniformLocation(gl, 'uNearPlane') + const sunPosLoc = shader.getUniformLocation(gl, 'uLightPosition') const sunColorLoc = shader.getUniformLocation(gl, 'uLightColor') @@ -154,6 +171,11 @@ class WaterTile extends Node { gl.uniform3fv(sunColorLoc, sun.color) gl.uniform1f(reflectivityLoc, this.reflectivity) gl.uniform1f(phaseLoc, this.phase) + gl.uniform1f(shininessLoc, this.shininess) + gl.uniform1f(damperLoc, this.shineDamper) + gl.uniform1f(distortLoc, this.distortionStrength) + gl.uniform1f(nearLoc, cam.nearPlane) + gl.uniform1f(farLoc, cam.farPlane) gl.uniform4fv(tintLoc, this.tint) gl.uniform1i(reflLoc, 0) gl.uniform1i(refrLoc, 1) @@ -178,9 +200,19 @@ class WaterTile extends Node { this.prepare(gl, shader, cam, sun) + gl.enable(gl.BLEND) + gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA) + this.mesh.prepare(gl, shader) this.mesh.draw(gl, shader) this.mesh.postdraw(gl, shader) + + gl.disable(gl.BLEND) + } + + update (dt) { + this.phase += 0.001 + if (this.phase >= 1) this.phase = 0 } } diff --git a/src/index.js b/src/index.js index fdd8e3e..1297983 100644 --- a/src/index.js +++ b/src/index.js @@ -28,7 +28,7 @@ async function pipeline () { let water = new WaterTile([100.0, 0.0, 100.0], 100.0) water.initialize(game.gl) - await water.useDUDV(game.gl, 'dudv') + await water.useDUDVMap(game.gl, 'dudv') await water.useNormalMap(game.gl, 'normalmap') let arialFont = await Font.fromFile('arial') @@ -43,9 +43,8 @@ async function pipeline () { new Dim4(-0.9, 0.0, 0.9, 0.0), new Dim4(0.1, 0.0, 0.1, 0.0)) ] // Nesting test - itms[0].addChild(new GUIText('this is example text!\nmulti line!', arialFont, 1.5, new Dim4(0.1, 0.0, -0.1, 0.0), new Dim4(1.0, 0.0, 0.3, 0.0), false)) - itms[0].children[0].color = [1.0, 0.0, 0.2] - itms[0].updateTransform() + itms[0].addChild(new GUIText('this project is coded by an idiot', arialFont, 1.5, new Dim4(0.1, 0.0, -0.1, 0.0), new Dim4(1.0, 0.0, 0.3, 0.0), false)) + itms[0].children[0].color = [0.0, 0.2, 1.0] // Create a height map based on OpenSimplex noise let hmap = new SimplexHeightMap(1, 1, 256, 50) @@ -76,9 +75,6 @@ async function pipeline () { // Load textures and generate a mesh await skybox.initialize(game.gl) - itms.push(new GUIImage(water.fbos.reflectionTexture, new Dim4(-0.9, 0.0, 0.9, 0.0), new Dim4(0.1, 0.0, 0.1, 0.0))) - itms.push(new GUIImage(water.fbos.refractionTexture, new Dim4(-0.3, 0.0, 0.9, 0.0), new Dim4(0.1, 0.0, 0.1, 0.0))) - // Update function for camera and terrain game.addUpdateFunction(function (dt) { if (game.input.isDown('w')) { @@ -101,10 +97,16 @@ async function pipeline () { // Update detail levels terrain.update(game.gl, cam) terrain.updateLODMesh(game.gl) + + // Ripple water + water.update(dt) }) function drawEverything (gl) { game.prepare() + // Draw the skybox + skyboxShader.use(gl) + skybox.draw(gl, skyboxShader, cam) // Use terrain shader terrainShader.use(gl) @@ -121,10 +123,6 @@ async function pipeline () { shader.use(gl) cam.draw(gl, shader) entity.draw(gl, shader) - - // Draw the skybox - skyboxShader.use(gl) - skybox.draw(gl, skyboxShader, cam) } // Render function for the triangle