some broken water code that needs fixing at some point

This commit is contained in:
Evert Prants 2019-12-29 14:58:53 +02:00
parent e103fa6e3b
commit bada6f86b4
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
11 changed files with 282 additions and 37 deletions

28
assets/shaders/water.fs Normal file
View File

@ -0,0 +1,28 @@
precision mediump float;
varying vec4 clipSpace;
uniform sampler2D reflectionTexture;
uniform sampler2D refractionTexture;
uniform sampler2D dudvMap;
varying vec2 uv;
const float waveStrength = 0.002;
void main() {
vec2 ndc = (clipSpace.xy / clipSpace.w) / 2.0 + 0.5;
vec2 refractTexCoords = vec2(ndc.x, ndc.y);
vec2 reflectTexCoords = vec2(ndc.x, 1.0 - ndc.y);
vec2 distortion1 = texture2D(dudvMap, uv).xy * 2.0 - 1.0 * waveStrength;
refractTexCoords += distortion1;
refractTexCoords = clamp(refractTexCoords, 0.001, 0.999);
reflectTexCoords += distortion1;
reflectTexCoords.x = clamp(reflectTexCoords.x, 0.001, 0.999);
reflectTexCoords.y = clamp(reflectTexCoords.y, -0.999, -0.001);
vec4 reflectColor = texture2D(reflectionTexture, reflectTexCoords);
vec4 refractColor = texture2D(refractionTexture, refractTexCoords);
gl_FragColor = mix(reflectColor, refractColor, 0.5);
}

18
assets/shaders/water.vs Normal file
View File

@ -0,0 +1,18 @@
precision mediump float;
attribute vec2 aVertexPosition;
uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix;
uniform mat4 uProjectionMatrix;
varying vec4 clipSpace;
varying vec2 uv;
const float tiling = 6.0;
void main() {
clipSpace = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition.x, 0.0, aVertexPosition.y, 1.0);
gl_Position = clipSpace;
uv = vec2(aVertexPosition.x/2.0+0.5,aVertexPosition.y/2.0+0.5) * tiling;
}

BIN
assets/textures/dudv.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 565 KiB

View File

@ -1,3 +1,4 @@
import Screen from './screen'
import { Node } from './components'
import { glMatrix, mat4, vec2, vec3 } from 'gl-matrix'
@ -101,8 +102,7 @@ class Camera extends Node {
}
updateProjection (gl) {
let aspect = gl.canvas.width / gl.canvas.height
mat4.perspective(this.projection, this.fov, aspect, this.nearPlane, this.farPlane)
mat4.perspective(this.projection, this.fov, Screen.aspectRatio, this.nearPlane, this.farPlane)
}
// Calculate the view matrix on-the-go

View File

@ -0,0 +1,153 @@
import { Node } from '../'
import { Mesh } from '../../mesh'
import { Texture } from '../../mesh/material'
import Screen from '../../screen'
import Resource from '../../resource'
class WaterFBOs {
constructor (reflectionWidth = 320, reflectionHeight = 180, refractionWidth = 1280, refractionHeight = 720) {
this.reflectionHeight = reflectionHeight
this.reflectionWidth = reflectionWidth
this.refractionHeight = refractionHeight
this.refractionWidth = refractionWidth
}
initialize (gl) {
this.initReflectionFrameBuffer(gl)
this.initRefractionFrameBuffer(gl)
}
initReflectionFrameBuffer (gl) {
this.reflectionFrameBuffer = this.createFrameBuffer(gl)
this.reflectionTexture = this.createTextureAttachment(gl, this.reflectionWidth, this.reflectionHeight)
this.reflectionDepthBuffer = this.createDepthBufferAttachment(gl, this.reflectionWidth, this.reflectionHeight)
this.unbindFrameBuffer(gl)
}
initRefractionFrameBuffer (gl) {
this.refractionFrameBuffer = this.createFrameBuffer(gl)
this.refractionTexture = this.createTextureAttachment(gl, this.refractionWidth, this.refractionHeight)
this.refractionDepthTexture = this.createDepthTextureAttachment(gl, this.refractionWidth, this.refractionHeight)
this.unbindFrameBuffer(gl)
}
createFrameBuffer (gl) {
// generate frame buffer
let frameBuffer = gl.createFramebuffer()
// create the framebuffer
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer)
// indicate that we will always render to color attachment 0
gl.drawBuffers.drawBuffersWEBGL([gl.COLOR_ATTACHMENT0])
return frameBuffer
}
unbindFrameBuffer (gl) {
gl.bindFramebuffer(gl.FRAMEBUFFER, null)
gl.viewport(0, 0, Screen.width, Screen.height)
}
bindFrameBuffer (gl, frameBuffer, width, height) {
gl.bindTexture(gl.TEXTURE_2D, null) // To make sure the texture isn't bound
gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer)
gl.viewport(0, 0, width, height)
}
// call before rendering to this FBO
bindReflectionFrameBuffer (gl) {
this.bindFrameBuffer(gl, this.reflectionFrameBuffer, this.reflectionWidth, this.reflectionHeight)
}
// call before rendering to this FBO
bindRefractionFrameBuffer (gl) {
this.bindFrameBuffer(gl, this.refractionFrameBuffer, this.refractionWidth, this.refractionHeight)
}
createTextureAttachment (gl, width, height) {
let texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, width, height, 0, gl.RGB, gl.UNSIGNED_BYTE, null)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
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.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0)
return texture
}
createDepthTextureAttachment (gl, width, height) {
let texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
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.LINEAR)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)
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
}
createDepthBufferAttachment (gl, width, height) {
let depthBuffer = gl.createRenderbuffer()
gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer)
gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height)
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.RENDERBUFFER, depthBuffer)
return depthBuffer
}
}
class WaterTile extends Node {
constructor (pos, scale, rot) {
super(pos, [scale, 0.0, scale], rot)
this.fbos = new WaterFBOs()
}
initialize (gl) {
this.mesh = Mesh.constructFromVertices(gl, [-1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1], 2)
this.fbos.initialize(gl)
}
async useDUDV (gl, file) {
this.dudv = await Texture.createTexture2D(gl, await Resource.loadImage(file + '.png'), false, gl.LINEAR)
}
reflect (gl, cam, render) {
this.fbos.bindReflectionFrameBuffer(gl)
let dist = 2 * (cam.pos[1] - this.pos[1])
cam.pos[1] -= dist
cam.rotation[1] *= -1
cam.updateTransform()
render(gl)
this.fbos.unbindFrameBuffer(gl)
cam.pos[1] += dist
cam.rotation[1] *= -1
cam.updateTransform()
}
refract (gl, cam, render) {
this.fbos.bindRefractionFrameBuffer(gl)
render(gl)
this.fbos.unbindFrameBuffer(gl)
}
draw (gl, shader) {
super.draw(gl, shader)
const transformLocation = shader.getUniformLocation(gl, 'uModelMatrix')
gl.uniformMatrix4fv(transformLocation, false, this.transform)
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, this.fbos.reflectionTexture)
gl.activeTexture(gl.TEXTURE1)
gl.bindTexture(gl.TEXTURE_2D, this.fbos.refractionTexture)
if (this.dudv) {
gl.activeTexture(gl.TEXTURE2)
gl.bindTexture(gl.TEXTURE_2D, this.dudv.id)
}
this.mesh.prepare(gl, shader)
this.mesh.draw(gl, shader)
this.mesh.postdraw(gl, shader)
}
}
export { WaterTile, WaterFBOs }

View File

View File

@ -6,7 +6,6 @@ import { Node2D } from './'
import { mat4 } from 'gl-matrix'
const aspectRatio = Screen.width / Screen.height
const PAD_TOP = 0
const PAD_LEFT = 1
const PAD_BOTTOM = 2
@ -46,6 +45,7 @@ class Word {
}
addCharacter (char) {
if (!char) return
this.characters.push(char)
this.width += char.xAdvance * this.fontSize
}
@ -74,7 +74,6 @@ class Line {
class FontFile {
constructor (name) {
this.name = name
this.values = {}
this.metadata = {}
}
@ -91,6 +90,7 @@ class FontFile {
return result
}
// Parse a .fnt file
readValues (data) {
let lines = data.split('\n')
for (let i in lines) {
@ -101,9 +101,7 @@ class FontFile {
for (let j in lineSplit) {
let valuePairs = lineSplit[j].split('=')
if (valuePairs.length === 2) {
let key = valuePairs[0]
let val = valuePairs[1]
lineValues[key] = val
lineValues[valuePairs[0]] = valuePairs[1]
}
}
@ -114,16 +112,11 @@ class FontFile {
} else if (lineSplit[0] === 'common') {
let lineHeightPixels = this.getValue(lineValues, 'lineHeight') - this.paddingHeight
this.vertPerPixelSize = LINE_HEIGHT / lineHeightPixels
this.horizPixelSize = this.vertPerPixelSize / aspectRatio
this.horizPixelSize = this.vertPerPixelSize / Screen.aspectRatio
this.scaleWidth = this.getValue(lineValues, 'scaleW')
} else if (lineSplit[0] === 'char') {
let c = this.loadCharacter(lineValues, this.getValue(this.values, 'scaleW'))
let c = this.loadCharacter(lineValues, this.scaleWidth)
if (c) this.metadata[c.id] = c
continue
} else if (lineSplit[0] === 'kernings' || lineSplit[0] === 'kerning' || lineSplit[0] === 'chars') {
continue
}
for (let j in lineValues) {
this.values[j] = lineValues[j]
}
}
}
@ -156,7 +149,6 @@ class FontFile {
let load = await Resource.GET('/assets/fonts/' + fontName + '.fnt')
let file = new FontFile(fontName)
file.readValues(load)
console.log(file)
return file
}
}
@ -168,21 +160,25 @@ class Font {
this.texture = null
}
// Load font data from a .fnt file and create a Font object with it
static async fromFile (name) {
let meta = await FontFile.fromFile(name)
return new Font(name, meta)
}
// Load font texture
async loadTextures (gl) {
this.texture = await Texture.createTexture2D(gl, await Resource.loadImage('/assets/fonts/' + this.name + '.png'), false, gl.LINEAR)
}
// Create a renderable mesh for a text
createTextMesh (gl, text) {
let lines = this.createStructure(text)
let data = this.createQuadVertices(text, lines)
return Mesh.constructFromVerticesUVs(gl, data.vertices, data.textureCoords)
}
// Create a structure of lines, words and characters in order to generate the vertices properly
createStructure (text) {
let chars = text.asCharacters
let lines = []
@ -221,6 +217,7 @@ class Font {
return lines
}
// Add final word
completeStructure (lines, currentLine, currentWord, text) {
let added = currentLine.attemptToAddWord(currentWord)
if (!added) {
@ -232,6 +229,7 @@ class Font {
return lines
}
// Create text vertices
createQuadVertices (text, lines) {
text.lines = lines.length
let cursorX = 0
@ -259,6 +257,7 @@ class Font {
return { vertices, textureCoords }
}
// Create text vertex coordinates for a specific character
addVerticesForCharacter (cursorX, cursorY, character, fontSize, vertices) {
let x = cursorX + (character.xOffset * fontSize)
let y = cursorY + (character.yOffset * fontSize)
@ -271,6 +270,7 @@ class Font {
this.addVertices(vertices, properX, properY, properMaxX, properMaxY)
}
// Create text vertex coordinates
addVertices (vertices, x, y, maxX, maxY) {
vertices.push(x)
vertices.push(y)
@ -286,6 +286,7 @@ class Font {
vertices.push(y)
}
// Create text texture coordinates
addTexCoords (texCoords, x, y, maxX, maxY) {
texCoords.push(x)
texCoords.push(y)
@ -312,6 +313,7 @@ class GUIText extends Node2D {
this.color = [0.0, 0.0, 0.0]
}
// Do not scale the transform like we do with regular GUIs
updateTransform () {
let matrix = mat4.create()
mat4.translate(matrix, matrix, [this.pos[0], this.pos[2], 0.0])
@ -343,6 +345,7 @@ class GUIText extends Node2D {
return this.size[2]
}
// Return all characters as their ascii code
get asCharacters () {
let chars = []
for (let i = 0; i < this.text.length; i++) {
@ -352,6 +355,8 @@ class GUIText extends Node2D {
}
draw (gl, shader) {
// Let only the font shader be used to render text
if (shader.name !== 'font') return super.draw(gl, shader)
const transformLocation = shader.getUniformLocation(gl, 'uTransformation')
const colorLocation = shader.getUniformLocation(gl, 'uColor')
gl.uniformMatrix4fv(transformLocation, false, this.transform)
@ -369,8 +374,8 @@ class FontRenderer {
for (let i in nodes) {
let node = nodes[i]
if (!(node instanceof GUIText)) {
if (node.children) {
textNodes.concat(this.discoverTextNodes(node.children))
if (node.children.length) {
textNodes = textNodes.concat(this.discoverTextNodes(node.children))
}
continue
}
@ -379,7 +384,8 @@ class FontRenderer {
return textNodes
}
draw (gl, nodes) {
draw (gl, cam, nodes) {
// Discover all nodes in the array that are texts
let fontPairs = {}
let textNodes = this.discoverTextNodes(nodes)
for (let i in textNodes) {
@ -399,6 +405,7 @@ class FontRenderer {
}
}
// Start rendering individual text arrays
this.shader.use(gl)
gl.enable(gl.BLEND)
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA)
@ -406,8 +413,10 @@ class FontRenderer {
for (let i in fontPairs) {
let texts = fontPairs[i]
let font = this.fonts[i]
// Set font's map as the texture
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(font.texture.type, font.texture.id)
// Draw all texts
for (let j in texts) {
let text = texts[j]
text.draw(gl, this.shader)

View File

@ -175,7 +175,11 @@ class GUIImage extends Node2D {
super.draw(gl, shader, quad)
if (!this.active) return
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(this.texture.type, this.texture.id)
if (!this.texture.type) {
gl.bindTexture(gl.TEXTURE_2D, this.texture)
} else {
gl.bindTexture(this.texture.type, this.texture.id)
}
// Set transformation matrix
const transformLocation = shader.getUniformLocation(gl, 'uTransformationMatrix')
@ -199,7 +203,7 @@ class GUIRenderer {
return this.quad
}
draw (gl, nodes) {
draw (gl, cam, nodes) {
if (typeof nodes !== 'object') nodes = [ nodes ]
this.shader.use(gl)
this.quad.prepare(gl, this.shader)

View File

@ -35,7 +35,7 @@ class Engine {
return this.screen.gl
}
render () {
prepare () {
// Set clear color to black, fully opaque
gl.clearColor(0.0, 0.0, 0.0, 1.0)
@ -48,7 +48,9 @@ class Engine {
// Enable back-face culling
gl.enable(gl.CULL_FACE)
gl.cullFace(gl.BACK)
}
render () {
// Render functions
for (let i in this.rst) {
this.rst[i](gl)
@ -98,4 +100,4 @@ class Engine {
}
}
export default Engine
export default new Engine()

View File

@ -11,11 +11,8 @@ class Screen {
return
}
this.loadExtensions()
document.body.appendChild(this._el)
window.addEventListener('resize', (e) => {
this.resize()
}, false)
}
get gl () {
@ -31,6 +28,12 @@ class Screen {
this._el.height = window.innerHeight
}
loadExtensions () {
this._gl.drawBuffers = this._gl.getExtension('WEBGL_draw_buffers')
this._gl.getExtension('WEBGL_depth_texture')
this._gl.getExtension('OES_texture_float')
}
get width () {
return this._el.width
}
@ -38,6 +41,10 @@ class Screen {
get height () {
return this._el.height
}
get aspectRatio () {
return this.width / this.height
}
}
export default new Screen()

View File

@ -6,12 +6,13 @@ import loadMesh from './engine/mesh/loader'
import { Environment } from './engine/environment'
import { LODTerrain } from './engine/components/terrain/lod'
import { Skybox } from './engine/components/skybox'
// import { WaterTile } from './engine/components/water'
import { SimplexHeightMap } from './engine/components/terrain/heightmap'
import { Material, Texture } from './engine/mesh/material'
import { GUIRenderer, GUIImage, Dim4 } from './engine/gui'
import { FontRenderer, GUIText, Font } from './engine/gui/font'
let game = new Engine()
let game = Engine
let env = new Environment()
let gui = new GUIRenderer()
let fnt = new FontRenderer()
@ -21,9 +22,14 @@ async function pipeline () {
let shader = await game.shaders.createShaderFromFiles(game.gl, 'basic', false)
let terrainShader = await game.shaders.createShaderFromFiles(game.gl, 'terrain', false)
let skyboxShader = await game.shaders.createShaderFromFiles(game.gl, 'skybox', false)
// let waterShader = await game.shaders.createShaderFromFiles(game.gl, 'water', false)
entity.setRotation([0.0, 0.0, -90.0])
// let water = new WaterTile([100.0, 0.0, 100.0], 100.0)
// water.initialize(game.gl)
// await water.useDUDV(game.gl, 'dudv')
let arialFont = await Font.fromFile('arial')
await arialFont.loadTextures(game.gl)
@ -33,10 +39,12 @@ async function pipeline () {
let itms = [
new GUIImage(await Texture.createTexture2D(game.gl, await Resource.loadImage('noisy.png'), false, game.gl.LINEAR),
new Dim4(-0.9, 0.0, 0.9, 0.0), new Dim4(0.1, 0.0, 0.1, 0.0)),
new GUIText('this is example text!\nmulti line!', arialFont, 2, new Dim4(0.1, 0.0, -0.2, 0.0), new Dim4(1.0, 0.0, 0.3, 0.0), true)
new Dim4(-0.9, 0.0, 0.9, 0.0), new Dim4(0.1, 0.0, 0.1, 0.0))
]
itms[1].color = [1.0, 0.0, 0.2]
// 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()
// Create a height map based on OpenSimplex noise
let hmap = new SimplexHeightMap(1, 1, 256, 50)
@ -58,7 +66,7 @@ async function pipeline () {
terrain.setMaterial(material)
// Create and initialize the camera
let cam = new Camera([-200.0, 1.0, 0.0])
let cam = new Camera([-32.0, 100.0, -32.0], [0.8, -0.6, 0.0])
cam.updateProjection(game.gl)
// Create skybox
@ -67,6 +75,9 @@ 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')) {
@ -91,8 +102,9 @@ async function pipeline () {
terrain.updateLODMesh(game.gl)
})
// Render function for the triangle
game.addRenderFunction(function (gl) {
function drawEverything (gl) {
game.prepare()
shader.use(gl)
cam.draw(gl, shader)
entity.draw(gl, shader)
@ -108,14 +120,26 @@ async function pipeline () {
// Draw terrain
terrain.draw(gl, terrainShader)
// Draw the skybox
skyboxShader.use(gl)
skybox.draw(gl, skyboxShader, cam)
})
}
// Render function for the triangle
game.addRenderFunction(function (gl) {
gui.draw(gl, itms)
fnt.draw(gl, itms)
// water.reflect(gl, cam, drawEverything)
// water.refract(gl, cam, drawEverything)
drawEverything(gl)
// waterShader.use(gl)
// cam.draw(gl, waterShader)
// water.draw(gl, waterShader)
// Draw GUIs
gui.draw(gl, cam, itms)
fnt.draw(gl, cam, itms)
})
game.startGameLoop()