mediocre frustum culling, very simple biomes

This commit is contained in:
Evert Prants 2020-04-03 19:50:08 +03:00
parent 26c822366d
commit f0a2eacd41
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
6 changed files with 163 additions and 33 deletions

View File

@ -3,13 +3,11 @@ precision mediump float;
uniform float g; uniform float g;
uniform float g2; uniform float g2;
uniform sampler2D texture0;
varying vec3 c0; varying vec3 c0;
varying vec3 c1; varying vec3 c1;
varying vec2 vUV; varying vec3 vColor;
void main (void) { void main (void) {
vec3 diffuse = texture2D(texture0, vUV).xyz; gl_FragColor = vec4(c1, 1.0) + vec4(vColor * c0, 1.0);
gl_FragColor = vec4(c1, 1.0) + vec4(diffuse * c0, 1.0);
} }

View File

@ -2,7 +2,7 @@ precision mediump float;
attribute vec3 aVertexPosition; attribute vec3 aVertexPosition;
attribute vec3 aNormal; attribute vec3 aNormal;
attribute vec2 aTexCoords; attribute vec3 aColor;
uniform vec3 v3CameraPosition; // The camera position uniform vec3 v3CameraPosition; // The camera position
uniform vec3 v3LightPosition; // The direction vector to the light source uniform vec3 v3LightPosition; // The direction vector to the light source
@ -27,7 +27,7 @@ const float fSamples = 3.0;
varying vec3 c0; varying vec3 c0;
varying vec3 c1; varying vec3 c1;
varying vec3 vNormal; varying vec3 vNormal;
varying vec2 vUV; varying vec3 vColor;
uniform mat4 uModelMatrix; uniform mat4 uModelMatrix;
uniform mat4 uViewMatrix; uniform mat4 uViewMatrix;
@ -86,6 +86,6 @@ void main(void) {
c1 = v3FrontColor * (v3InvWavelength * fKrESun + fKmESun); c1 = v3FrontColor * (v3InvWavelength * fKrESun + fKmESun);
gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition,1); gl_Position = uProjectionMatrix * uViewMatrix * uModelMatrix * vec4(aVertexPosition,1);
vUV = aTexCoords; vColor = aColor;
vNormal = aNormal; vNormal = aNormal;
} }

View File

@ -1,6 +1,6 @@
import Screen from './screen' import Screen from './screen'
import { Node } from './components' import { Node } from './components'
import { glMatrix, mat4, vec2, vec3 } from 'gl-matrix' import { glMatrix, mat4, vec2, vec3, vec4 } from 'gl-matrix'
const SPEED = 100.0 const SPEED = 100.0
const SENSITIVTY = 100.0 const SENSITIVTY = 100.0
@ -8,6 +8,92 @@ const FOV = 45.0
const ZNEAR = 0.1 const ZNEAR = 0.1
const ZFAR = 10000.0 const ZFAR = 10000.0
class Frustum {
constructor () {
this.planes = []
}
construct (projectionMatrix) {
const me = projectionMatrix
this.planes = [
vec4.normalize([], [me[3] - me[0], me[7] - me[4], me[11] - me[8], me[15] - me[12]]),
vec4.normalize([], [me[3] + me[0], me[7] + me[4], me[11] + me[8], me[15] + me[12]]),
vec4.normalize([], [me[3] + me[1], me[7] + me[5], me[11] + me[9], me[15] + me[13]]),
vec4.normalize([], [me[3] - me[1], me[7] - me[5], me[11] - me[9], me[15] - me[13]]),
vec4.normalize([], [me[3] - me[2], me[7] - me[6], me[11] - me[10], me[15] - me[14]]),
vec4.normalize([], [me[3] + me[2], me[7] + me[6], me[11] + me[10], me[15] + me[14]])
]
}
// Calculates the closest distance from a given point to a given clipping plane
distanceToPlane (plane, point) {
return vec3.dot(this.planes[plane], point) + this.planes[plane][3]
}
containsPoint (point) {
// For each plane; return outside if the point is behind the plane
for (let i = 0; i < 6; i++) {
if (this.distanceToPlane(i, point) <= 0.0) return false
}
// Return inside
return true
}
containsSphere (position, radius) {
// Plane counter
let planeCount = 0
// Use the point-to-plane distance to calculate the number of planes the sphere is in front of
for (let i = 0; i < 6; i++) {
const distance = this.distanceToPlane(i, position)
if (distance <= -radius) {
return 0
} else if (distance > radius) {
planeCount++
}
}
// Return inside if in front of all planes; otherwise intersecting
return planeCount === 6 ? 1 : 2
}
containsBox (min, max) {
// Build a vector holding all box corners
const points = []
for (let i = 0; i < 8; i++) {
points.push([i < 4 ? max[0] : min[0], i % 4 < 2 ? max[1] : min[1], i % 2 ? max[2] : min[2]])
}
// Test the box as a polygon
return this.containsPolygon(points)
}
containsPolygon (points) {
// Plane counter
let planeCount = 0
// Use the point-to-plane distance to calculate the number of planes the polygon is in front of
for (let i = 0; i < 6; i++) {
let pointCount = 0
for (let j = 0; j < points.length; j++) {
if (this.distanceToPlane(i, points[j]) > 0.0) {
pointCount++
}
}
if (pointCount === 0) {
return 0
} else if (pointCount === points.length) {
planeCount++
}
}
// Return inside if in front of all planes; otherwise intersecting
return planeCount === 6 ? 1 : 2
}
}
class Camera extends Node { class Camera extends Node {
constructor (pos, rotation) { constructor (pos, rotation) {
super(pos, null, rotation) super(pos, null, rotation)
@ -27,6 +113,9 @@ class Camera extends Node {
this.nearPlane = ZNEAR this.nearPlane = ZNEAR
this.farPlane = ZFAR this.farPlane = ZFAR
// Frustum planes
this.frustum = new Frustum()
this.updateTransform() this.updateTransform()
} }
@ -99,10 +188,13 @@ class Camera extends Node {
vec3.cross(upCross, this.right, this.front) vec3.cross(upCross, this.right, this.front)
vec3.normalize(this.up, upCross) vec3.normalize(this.up, upCross)
this.frustum.construct(mat4.multiply([], this.projection, this.view))
} }
updateProjection (gl) { updateProjection (gl) {
mat4.perspective(this.projection, this.fov, Screen.aspectRatio, this.nearPlane, this.farPlane) mat4.perspective(this.projection, this.fov, Screen.aspectRatio, this.nearPlane, this.farPlane)
this.updateTransform()
} }
// Calculate the view matrix on-the-go // Calculate the view matrix on-the-go

View File

@ -1,13 +1,13 @@
import { MeshInstance } from '../'
import Sphere from '../../mesh/geometry/sphere' import Sphere from '../../mesh/geometry/sphere'
import { vec3 } from 'gl-matrix' import { vec3 } from 'gl-matrix'
import { subv3, normalv3 } from '../../utility' import { subv3, normalv3 } from '../../utility'
class Atmosphere extends MeshInstance { class Atmosphere {
constructor (pos, innerRadius, outerRadius, wavelength = [0.650, 0.570, 0.475]) { constructor (planet, outerRadius, wavelength = [0.650, 0.570, 0.475]) {
super(Sphere.new(outerRadius, 200, 200), pos) this.mesh = Sphere.new(outerRadius, 200, 200)
this.planet = planet
this.outerRadius = outerRadius this.outerRadius = outerRadius
this.innerRadius = innerRadius this.innerRadius = planet.generator.radius
this.wavelength = wavelength this.wavelength = wavelength
this.Kr = 0.0025 this.Kr = 0.0025
@ -19,7 +19,7 @@ class Atmosphere extends MeshInstance {
} }
setUniforms (gl, shader, camera, sun) { setUniforms (gl, shader, camera, sun) {
const camHeight = vec3.length(subv3(camera.pos, this.pos)) const camHeight = vec3.length(subv3(camera.pos, this.planet.origin))
const invWavelength = [1 / Math.pow(this.wavelength[0], 4), 1 / Math.pow(this.wavelength[1], 4), 1 / Math.pow(this.wavelength[2], 4)] const invWavelength = [1 / Math.pow(this.wavelength[0], 4), 1 / Math.pow(this.wavelength[1], 4), 1 / Math.pow(this.wavelength[2], 4)]
gl.uniform3fv(shader.getUniformLocation(gl, 'v3CameraPosition'), camera.pos) gl.uniform3fv(shader.getUniformLocation(gl, 'v3CameraPosition'), camera.pos)
gl.uniform3fv(shader.getUniformLocation(gl, 'v3LightPosition'), normalv3(sun.pos)) gl.uniform3fv(shader.getUniformLocation(gl, 'v3LightPosition'), normalv3(sun.pos))
@ -43,7 +43,7 @@ class Atmosphere extends MeshInstance {
draw (gl, shader, camera, sun) { draw (gl, shader, camera, sun) {
// Set model transform matrix uniform // Set model transform matrix uniform
gl.uniformMatrix4fv(shader.getUniformLocation(gl, 'uModelMatrix'), false, this.transform) gl.uniformMatrix4fv(shader.getUniformLocation(gl, 'uModelMatrix'), false, this.planet.transform)
this.setUniforms(gl, shader, camera, sun) this.setUniforms(gl, shader, camera, sun)
// Draw the mesh // Draw the mesh

View File

@ -4,15 +4,38 @@ import { mat4, vec3 } from 'gl-matrix'
import { subv3, mulv3, addv3, divv3, normalv3, crossv3 } from '../../utility' import { subv3, mulv3, addv3, divv3, normalv3, crossv3 } from '../../utility'
import Screen from '../../screen' import Screen from '../../screen'
const lodMax = 11 const lodMax = 8
class PlanetGenerator { class PlanetGenerator {
constructor (resolution, radius, noise) { constructor (radius, noise, waterHeight = 0.3) {
this.resolution = resolution
this.radius = radius this.radius = radius
this.noise = noise this.noise = noise
this.moisture = Object.assign({ seed: noise.seed / 2 }, noise)
}
PlanetIndexBuffers.generate(resolution) getHeight (detail, normal) {
return this.noise.getNoise3D(detail, normal[0], normal[1], normal[2])
}
getBiome (detail, normal) {
const heightAtPoint = this.getHeight(detail, normal)
// const moistureAtPoint = this.moisture.getNoise3D(5, normal)
// 0 - hot, 1 - cold
const equatorTemp = 1
// 0 - hot, 1 - cold
const poleTemp = 0.4
// TODO: pole settings for planet
const distanceFromPoles = Math.abs(vec3.dot(normal, [0.0, 1.0, 0.0]))
let e = heightAtPoint
// Calculate temperature for the poles
e = (e * e + poleTemp + (equatorTemp - poleTemp) * distanceFromPoles)
if (heightAtPoint < 0.1) return [1 / 255, 56 / 255, 104 / 255] // Water
else if (e <= 0.8) return [44 / 255, 50 / 255, 29 / 255] // Grass
else if (e < 0.9) return [50 / 255, 50 / 255, 50 / 255] // Stone
else return [1, 1, 1] // Snow
} }
} }
@ -21,7 +44,7 @@ const PlanetIndexBuffers = {
if (sideResolution <= 1 || sideResolution % 2 === 0) { if (sideResolution <= 1 || sideResolution % 2 === 0) {
throw new Error('Resolution must be higher than 1 and an odd number.') throw new Error('Resolution must be higher than 1 and an odd number.')
} }
PlanetIndexBuffers.resolution = sideResolution
PlanetIndexBuffers.base = PlanetIndexBuffers.generateBuffer(sideResolution) PlanetIndexBuffers.base = PlanetIndexBuffers.generateBuffer(sideResolution)
PlanetIndexBuffers.fixT = PlanetIndexBuffers.generateBuffer(sideResolution, true, false, false, false) PlanetIndexBuffers.fixT = PlanetIndexBuffers.generateBuffer(sideResolution, true, false, false, false)
PlanetIndexBuffers.fixTR = PlanetIndexBuffers.generateBuffer(sideResolution, true, true, false, false) PlanetIndexBuffers.fixTR = PlanetIndexBuffers.generateBuffer(sideResolution, true, true, false, false)
@ -104,6 +127,7 @@ class CubeFace {
this.level = level this.level = level
this.generated = false this.generated = false
this.visible = true
this.planet = planet this.planet = planet
this.generator = planet.generator this.generator = planet.generator
@ -125,9 +149,10 @@ class CubeFace {
generate () { generate () {
if (this.generated) return if (this.generated) return
const sideResolution = this.generator.resolution const sideResolution = PlanetIndexBuffers.resolution
const vertices = [] const vertices = []
const normals = [] const normals = []
const colors = []
const textureCoords = [] const textureCoords = []
const radius = this.generator.radius const radius = this.generator.radius
@ -148,8 +173,10 @@ class CubeFace {
// Normalize and multiply by radius to create a spherical mesh // Normalize and multiply by radius to create a spherical mesh
const normal = normalv3(vertex) const normal = normalv3(vertex)
const pointHeight = this.generator.noise.getNoise3D(this.level + 1, normal[0], normal[1], normal[2]) const pointHeight = this.generator.getHeight(this.level + 1, normal)
const pos = mulv3(normal, pointHeight * 2 + radius) const pos = mulv3(normal, pointHeight * 10 + radius)
const pointBiome = this.generator.getBiome(this.level + 1, normal)
vertices[vertexPointer * 3] = pos[0] vertices[vertexPointer * 3] = pos[0]
vertices[vertexPointer * 3 + 1] = pos[1] vertices[vertexPointer * 3 + 1] = pos[1]
@ -157,6 +184,9 @@ class CubeFace {
normals[vertexPointer * 3] = normal[0] normals[vertexPointer * 3] = normal[0]
normals[vertexPointer * 3 + 1] = normal[1] normals[vertexPointer * 3 + 1] = normal[1]
normals[vertexPointer * 3 + 2] = normal[2] normals[vertexPointer * 3 + 2] = normal[2]
colors[vertexPointer * 3] = pointBiome[0]
colors[vertexPointer * 3 + 1] = pointBiome[1]
colors[vertexPointer * 3 + 2] = pointBiome[2]
textureCoords[vertexPointer * 2] = i * (1 / sideResolution) textureCoords[vertexPointer * 2] = i * (1 / sideResolution)
textureCoords[vertexPointer * 2 + 1] = j * (1 / sideResolution) textureCoords[vertexPointer * 2 + 1] = j * (1 / sideResolution)
@ -170,6 +200,7 @@ class CubeFace {
Mesh.generateNormals(normals, PlanetIndexBuffers.base.indices, vertices) Mesh.generateNormals(normals, PlanetIndexBuffers.base.indices, vertices)
this.mesh = Mesh.construct(Screen.gl, vertices, null, textureCoords, normals) this.mesh = Mesh.construct(Screen.gl, vertices, null, textureCoords, normals)
this.mesh.cbuffer = Mesh.loadToBuffer(Screen.gl, Screen.gl.ARRAY_BUFFER, new Float32Array(colors))
this.bounds = BoundingBox.fromMesh(this.mesh) this.bounds = BoundingBox.fromMesh(this.mesh)
this.generated = true this.generated = true
} }
@ -231,22 +262,31 @@ class CubeFace {
return return
} }
if (!this.visible) return
CubeFace.determineIndexBuffer(this) CubeFace.determineIndexBuffer(this)
this.mesh.prepare(gl, shader) this.mesh.prepare(gl, shader)
if (this.mesh.cbuffer && shader.hasAttribute(gl, 'aColor')) {
gl.bindBuffer(gl.ARRAY_BUFFER, this.mesh.cbuffer)
shader.setAttribute(gl, 'aColor', 3, false, 3 * Float32Array.BYTES_PER_ELEMENT, 0)
this.mesh._bufferCount++
}
this.mesh.draw(gl, shader) this.mesh.draw(gl, shader)
} }
update (camera, dt) { update (camera, dt) {
if (!this.center) return if (!this.center) return
this.visible = camera.frustum.containsBox(this.bounds.min, this.bounds.max)
const camToOrigin = vec3.distance(camera.pos, this.center) const camToOrigin = vec3.distance(camera.pos, this.center)
const divisionLevel = Math.pow(2, this.level) const divisionLevel = Math.pow(2, this.level)
const splitDistance = this.generator.radius / divisionLevel const splitDistance = this.generator.radius / divisionLevel
if (camToOrigin < splitDistance * 2 && this.children.length === 0) { if (camToOrigin < splitDistance * 5 && this.children.length === 0) {
this.subdivide() this.subdivide()
return return
} else if (camToOrigin > splitDistance * 2.5 && this.children.length > 0) { } else if (camToOrigin > splitDistance * 5.5 && this.children.length > 0) {
this.merge() this.merge()
return return
} }
@ -367,7 +407,6 @@ class CubeFace {
static determineIndexBuffer (face) { static determineIndexBuffer (face) {
let buffer = PlanetIndexBuffers.base let buffer = PlanetIndexBuffers.base
if (face.level <= 1) return PlanetIndexBuffers.setBuffer(face.mesh, buffer)
switch (face.index) { switch (face.index) {
// Top left corner // Top left corner
case 0: case 0:
@ -458,7 +497,7 @@ class CubePlanet {
} }
static drawPlanetAtmosphere (gl, planet, atmosphere, camera, sun, atmosShader, planetShader, surfaceShader, surfaceEnvironment) { static drawPlanetAtmosphere (gl, planet, atmosphere, camera, sun, atmosShader, planetShader, surfaceShader, surfaceEnvironment) {
const height = vec3.length(subv3(camera.pos, atmosphere.pos)) const height = vec3.length(subv3(camera.pos, planet.origin))
// Render the atmosphere // Render the atmosphere
atmosShader.use(gl) atmosShader.use(gl)
@ -486,4 +525,4 @@ class CubePlanet {
} }
} }
export { CubePlanet, CubeFace, PlanetGenerator } export { CubePlanet, CubeFace, PlanetGenerator, PlanetIndexBuffers }

View File

@ -16,7 +16,7 @@ import { GUIRenderer, GUIImage, Dim4 } from './engine/gui'
import { FontRenderer, GUIText, Font } from './engine/gui/font' import { FontRenderer, GUIText, Font } from './engine/gui/font'
import { VoxelWorld, VoxelGenerator } from './engine/voxel' import { VoxelWorld, VoxelGenerator } from './engine/voxel'
import { CubePlanet, PlanetGenerator } from './engine/components/planet' import { CubePlanet, PlanetGenerator, PlanetIndexBuffers } from './engine/components/planet'
import Atmosphere from './engine/components/planet/atmosphere' import Atmosphere from './engine/components/planet/atmosphere'
const game = Engine const game = Engine
@ -63,7 +63,7 @@ async function pipeline () {
itms[0].color = [0.0, 0.2, 1.0] itms[0].color = [0.0, 0.2, 1.0]
// Create a height map based on OpenSimplex noise // Create a height map based on OpenSimplex noise
const hmap = new SimplexHeightMap(50, 64, 64, 0.2) const hmap = new SimplexHeightMap(1, 20, 10, 0.2)
// Create a terrain instance // Create a terrain instance
// let terrain = new LODTerrain([0.0, 0.0, 0.0], 1024, 1024, 850, 4) // let terrain = new LODTerrain([0.0, 0.0, 0.0], 1024, 1024, 850, 4)
@ -83,7 +83,7 @@ async function pipeline () {
// Create and initialize the camera // Create and initialize the camera
// [-1300.0, 1325.0, -1300.0], [0.8, -0.6, 0.0] // [-1300.0, 1325.0, -1300.0], [0.8, -0.6, 0.0]
const cam = new Camera([-37, 1107, -1583], [1.5, -0.6, 0]) const cam = new Camera([0, 0, -2500], [1.5, -0, 0])
cam.updateProjection(game.gl) cam.updateProjection(game.gl)
// Create skybox // Create skybox
@ -97,8 +97,9 @@ async function pipeline () {
// let block = new VoxelWorld(voxgen) // let block = new VoxelWorld(voxgen)
// Planet test // Planet test
const planet = new CubePlanet([0.0, 0.0, 0.0], new PlanetGenerator(15, 1000, hmap)) PlanetIndexBuffers.generate(15)
const atmosphere = new Atmosphere([0.0, 0.0, 0.0], 1000, 1050, [0.650, 0.570, 0.475]) const planet = new CubePlanet([0.0, 0.0, 0.0], new PlanetGenerator(1000, hmap))
const atmosphere = new Atmosphere(planet, 1050, [0.650, 0.570, 0.475])
planet.material = material planet.material = material