Created
May 11, 2019 23:26
-
-
Save sneppy/42aa5fc75e56c8738b1ae122c602aa90 to your computer and use it in GitHub Desktop.
Procedural terrain generation + volumetric fog rendering
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////////////////////////////////////////// | |
// The final part of the deferred shading pipeline | |
// is made with a compute shader (because I'm too | |
// lazy to draw a quad and write another .vert/.frag) | |
////////////////////////////////////////////////// | |
#version 450 core | |
uniform float time; | |
uniform float samplingStep; | |
uniform ivec2 fboSize; | |
uniform mat4 projMatrix; | |
uniform mat4 viewMatrix; | |
uniform vec3 cameraLocation; | |
layout(binding = 10) uniform sampler3D fogData; | |
layout(binding = 0) uniform sampler2D inPos; | |
layout(binding = 1) uniform sampler2D inNorm; | |
layout(binding = 2) uniform sampler2D inColor; | |
layout(binding = 3) uniform sampler2D inDepth; | |
layout(binding = 0, rgba32f) writeonly uniform image2D backbuffer; | |
float sampleNoise(vec3 p) | |
{ | |
return texture(fogData, p + vec3(time, 0.f, time) * 0.05f).r; | |
} | |
float octaves(vec3 p, int numOctaves) | |
{ | |
float f = 1.f; | |
float s = 0.5f; | |
float v = 0.f; | |
for (int i = 0; i < numOctaves; ++i, f *= 2.f, s *= 0.5f) | |
v += sampleNoise(p * f) * s; | |
return v; | |
} | |
float monoOctaves(vec3 p, int numOctaves) | |
{ | |
float f = 1.f; | |
float s = 1.f; | |
float v = 0.f; | |
for (int i = 0; i < numOctaves; ++i, f *= 2.f, s *= 0.5f) | |
v += max(sampleNoise(p * f) * s, 0.f); | |
return v; | |
} | |
float sampleVolume(vec3 traceStart, vec3 traceEnd, float samplingStep, int numSamples) | |
{ | |
const vec3 ray = traceEnd - traceStart; | |
const vec3 dir = normalize(ray); | |
const float len = length(ray); | |
float val = 0.f; | |
float s = 0.5f; | |
for (float t = 1.f; numSamples > 0 && val < 1.f; t += samplingStep, s *= 1.f, --numSamples) | |
{ | |
const vec3 p = traceStart + t * dir; | |
val += sampleNoise(p / 4.f) * s; | |
} | |
val = min(val, 1.f); | |
return val; | |
} | |
vec3 sampleVolumeRgb(vec3 traceStart, vec3 traceEnd, float samplingStep, int numSamples) | |
{ | |
const vec3 ray = traceEnd - traceStart; | |
const vec3 dir = normalize(ray); | |
const float len = length(ray); | |
vec3 val = vec3(0.f); | |
float s = 0.5f; | |
for (float t = 0.4f; numSamples > 0 && t < len; t += samplingStep, s *= 0.9f, --numSamples) | |
{ | |
const vec3 p = traceStart + t * dir; | |
val += sampleNoise(p / 4.f) * normalize(0.5f + 0.5f * cos(p)) * s; | |
} | |
val = min(val, vec3(1.f)); | |
return val; | |
} | |
vec4 sampleVolumeRgba(vec3 traceStart, vec3 traceEnd, float samplingStep, int numSamples, float depth) | |
{ | |
const vec3 ray = traceEnd - traceStart; | |
const vec3 dir = normalize(ray); | |
const float len = min(length(ray), depth); | |
vec4 val = vec4(0.f); | |
float s = 0.5f; | |
float r = 1.1f; | |
for (float t = 0.2f; numSamples > 0 && t < len; t += samplingStep, samplingStep *= r, s *= 0.9f, --numSamples) | |
{ | |
const vec3 p = traceStart + t * dir; | |
val += sampleNoise(p / 4.f) * vec4(normalize(0.5f + 0.5f * cos(p / 2.f)), 1.f) * s; | |
} | |
val = min(val, vec4(1.f)); | |
return val; | |
} | |
vec4 sampleVolumeMCRgba(vec3 traceStart, vec3 traceEnd, float samplingStep, float depth, uint maxSteps) | |
{ | |
const vec3 ray = traceEnd - traceStart; | |
const vec3 dir = normalize(ray); | |
const float dist = length(ray); | |
const float len = min(dist, depth); | |
vec4 val = vec4(0.f); | |
float s = 0.f, t; | |
uint i = 0; | |
for (t = s; t < len && i < maxSteps; t += samplingStep, samplingStep *= 1.05f, ++i) | |
{ | |
const vec3 p = traceStart + t * dir; | |
val += sampleNoise(p / 4.f) * vec4(normalize(sin(p) * 0.5f + 0.5f), 1.f); | |
} | |
return min(val * (t - s) / i, vec4(1.f)); | |
} | |
struct Vertex | |
{ | |
vec3 pos; | |
vec3 norm; | |
} inVert; | |
/// Local work group setup | |
layout(local_size_x = 48, local_size_y = 27) in; | |
void main() | |
{ | |
// Soooo, this is a complete mess. | |
// Basically we first compute the fog value | |
// for this pixel. Then we use this value | |
// to drive an image distortion. | |
// The warped uv coordinates are used to | |
// lookup pixel position, normal and color. | |
// Then you can do normal light computation | |
// with those values (here there's just a naive | |
// directional light) | |
// Finally, fog and terrain are blended together | |
// using the depth map (you can also use the fog | |
// alpha channel, but the fog effect is much more subtle) | |
const vec2 uv = vec2(gl_GlobalInvocationID.xy) / vec2(fboSize); | |
mat4 invProjMatrix = inverse(viewMatrix); | |
vec2 clipCoords = vec2(gl_GlobalInvocationID.xy * 2) / vec2(fboSize) - 1.f; | |
vec4 nearPlane = (invProjMatrix * vec4(clipCoords, -1.f, 1.f)); | |
vec4 farPlane = (invProjMatrix * vec4(clipCoords, 1.f, 1.f)); | |
vec3 traceStart = nearPlane.xyz / nearPlane.w; | |
vec3 traceEnd = farPlane.xyz / farPlane.w; | |
float depth = texture(inDepth, uv).r; | |
const vec4 val = sampleVolumeMCRgba(traceStart, traceEnd, 0.05f, length(inVert.pos - cameraLocation), 32); | |
const vec2 wuv = vec2(gl_GlobalInvocationID.xy) / vec2(fboSize) + (val.a - 0.5f) / 8.f; | |
inVert.pos = texture(inPos, wuv).xyz; | |
inVert.norm = texture(inNorm, wuv).xyz; | |
depth = texture(inDepth, wuv).r; | |
const vec3 lightOrientation = normalize(vec3(0.5f, -1.f, -0.9f)); | |
const vec3 lightPos = vec3(0.f, 5.f, 0.f); | |
const vec3 lightColor = normalize(vec3(1.f, 0.9f, 0.85f)); | |
const float lightRadius = 20.f; | |
const float lightIntensity = 1.2f; | |
const float specularStrength = 0.85f; | |
const float specularFactor = 16.f; | |
const vec3 lightRay = inVert.pos - lightPos; | |
const vec3 lightDir = lightOrientation;//normalize(lightRay); | |
const float ligthDist = length(lightRay); | |
const vec3 reflectionDir = reflect(-lightDir, inVert.norm); | |
const vec3 viewDir = normalize(inVert.pos - cameraLocation); | |
const vec3 ambient = lightColor * 0.2f; | |
const vec3 albedo = (sin(inVert.pos) * 0.5f + 0.5f); | |
const float diffuse = max(dot(lightDir, -inVert.norm), 0.f) * lightIntensity; | |
const float specular = pow(max(dot(reflectionDir, viewDir), 0.f), 16.f) * specularStrength * exp(-ligthDist / lightRadius); | |
const vec3 finalColor = (ambient + diffuse + specular) * lightColor * albedo; | |
imageStore(backbuffer, ivec2(gl_GlobalInvocationID.xy), vec4(mix(finalColor, val.rgb * vec3(0.1f, 0.2f, 0.5f), pow(depth, 2.f)), 1.f)); | |
/// Compute noise value | |
//const float val = sampleVolume(traceStart, traceEnd, 0.12f, 32); | |
//imageStore(colorBuffer, ivec2(gl_GlobalInvocationID.xy), vec4(normalize(abs(traceStart) + 1.f) * val, 1.f)); | |
/* const vec4 val = sampleVolumeRgba(traceStart, traceEnd, 0.05f, 32, length(inVert.pos - cameraLocation)); | |
imageStore(backbuffer, ivec2(gl_GlobalInvocationID.xy), mix(vec4(finalColor, 1.f), val, depth)); */ | |
/* const vec4 val = sampleVolumeMCRgba(traceStart, traceEnd, 0.25f, length(inVert.pos - cameraLocation), 32); | |
imageStore(backbuffer, ivec2(gl_GlobalInvocationID.xy), mix(vec4(finalColor, 1.f), val, val.a)); */ | |
//imageStore(backbuffer, ivec2(gl_GlobalInvocationID.xy), texture(inDepth, vec2(gl_GlobalInvocationID.xy) / vec2(1280.f, 720.f)).rrrr); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////////////////////////////////////////// | |
// The fragment shader simply outputs position | |
// normal and color to the attached render targets. | |
////////////////////////////////////////////////// | |
#version 450 core | |
layout(location = 0) out vec3 outPos; | |
layout(location = 1) out vec3 outNorm; | |
layout(location = 2) out vec3 outColor; | |
in Vertex | |
{ | |
vec3 pos; | |
vec3 norm; | |
} inVert; | |
void main() | |
{ | |
// Output fragment data | |
outPos = inVert.pos; | |
outNorm = inVert.norm; | |
outColor = sin(inVert.pos) * 0.5f + 0.5f; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////////////////////////////////////////// | |
// A trivial vertex shader that passes global | |
// position and normals to the fragment shader. | |
// No need to transform normals for the viewMatrix | |
// is essentially just a translation (chunk offset) | |
////////////////////////////////////////////////// | |
#version 450 core | |
layout(location = 0) in vec4 inPos; | |
layout(location = 1) in vec3 inNorm; | |
uniform mat4 modelMatrix; | |
uniform mat4 viewMatrix; | |
out Vertex | |
{ | |
vec3 pos; | |
vec3 norm; | |
} outVert; | |
void main() | |
{ | |
vec4 wsPos = modelMatrix * inPos; | |
outVert.pos = vec3(wsPos); | |
outVert.norm = inNorm; | |
gl_Position = viewMatrix * wsPos; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////////////////////////////////////////// | |
// Essentially this is exactly the same compute | |
// shader that generates the terrain density | |
// map but the result is saved in a single | |
// 256^3 3D texture (you can try to push this | |
// size if your hardware can handle it) | |
////////////////////////////////////////////////// | |
#version 450 core | |
uniform float time; | |
uniform float samplingStep; | |
uniform ivec2 framebufferSize; | |
uniform mat4 projMatrix; | |
uniform mat4 viewMatrix; | |
struct PerlinData | |
{ | |
/// Permutation table | |
int perms[256]; | |
/// Gradients table | |
vec3 grads[256]; | |
}; | |
/// Perlin data buffer | |
layout(binding = 0, std430) buffer PerlinBuffer | |
{ | |
PerlinData tables[]; | |
}; | |
layout(binding = 0, r32f) writeonly uniform image3D volumeData; | |
/// Vertices of a [<0,0,0>, <1,1,1>] cube | |
const vec3 cubeCorners[] = { | |
vec3(0.f, 0.f, 0.f), | |
vec3(1.f, 0.f, 0.f), | |
vec3(0.f, 1.f, 0.f), | |
vec3(1.f, 1.f, 0.f), | |
vec3(0.f, 0.f, 1.f), | |
vec3(1.f, 0.f, 1.f), | |
vec3(0.f, 1.f, 1.f), | |
vec3(1.f, 1.f, 1.f), | |
}; | |
float gradient(vec3 p, ivec3 i, ivec3 period) | |
{ | |
const vec3 grad = tables[0].grads[tables[0].perms[(tables[0].perms[(tables[0].perms[i.x % period.x] + i.y) % period.y] + i.z) % period.z]]; | |
return dot(p, grad); | |
} | |
float sampleNoise(vec3 p, ivec3 period) | |
{ | |
vec3 i = floor(p); | |
vec3 t = fract(p); | |
// Soft curve | |
// @ref https://www.google.com/search?ei=tvSgXMDCGMvisAeL7bawAQ&q=x+*+x+*+%283+-+2+*+x%29&oq=x+*+x+*+%283+-+2+*+x%29&gs_l=psy-ab.3..0i13i30l2j0i22i30l8.4020.9555..9980...0.0..0.119.1704.14j5......0....1..gws-wiz.......0i71j35i39j0j0i67j0i22i10i30.e3qAa8TpAUc | |
vec3 w = t * t * (3.f - 2.f * t); | |
return mix( | |
mix( | |
mix( | |
gradient(t - cubeCorners[0], ivec3(i + cubeCorners[0]), period), | |
gradient(t - cubeCorners[1], ivec3(i + cubeCorners[1]), period), | |
w.x | |
), | |
mix( | |
gradient(t - cubeCorners[2], ivec3(i + cubeCorners[2]), period), | |
gradient(t - cubeCorners[3], ivec3(i + cubeCorners[3]), period), | |
w.x | |
), | |
w.y | |
), | |
mix( | |
mix( | |
gradient(t - cubeCorners[4], ivec3(i + cubeCorners[4]), period), | |
gradient(t - cubeCorners[5], ivec3(i + cubeCorners[5]), period), | |
w.x | |
), | |
mix( | |
gradient(t - cubeCorners[6], ivec3(i + cubeCorners[6]), period), | |
gradient(t - cubeCorners[7], ivec3(i + cubeCorners[7]), period), | |
w.x | |
), | |
w.y | |
), | |
w.z | |
); | |
} | |
float octaves(vec3 p, int numOctaves, ivec3 period) | |
{ | |
float f = 1.f; | |
float s = 0.5f; | |
float v = 0.f; | |
for (int i = 0; i < numOctaves; ++i, f *= 2.f, s *= 0.5f) | |
v += sampleNoise(p * f, period) * s; | |
return v; | |
} | |
float monoOctaves(vec3 p, int numOctaves, ivec3 period) | |
{ | |
float f = 1.f; | |
float s = 1.f; | |
ivec3 r = period; | |
float v = 0.f; | |
for (int i = 0; i < numOctaves; ++i, f *= 2.f, s *= 0.5f, period *= 2) | |
v += max(sampleNoise(p * f, period) * s, 0.f); | |
return v; | |
} | |
layout(local_size_x = 8, local_size_y = 8, local_size_z = 8) in; | |
void main() | |
{ | |
// Get global position and sample | |
const vec3 p = vec3(gl_GlobalInvocationID) / 64.f; | |
const float val = monoOctaves(p + monoOctaves(p, 4, ivec3(4)), 9, ivec3(4)); | |
// Store value | |
imageStore(volumeData, ivec3(gl_GlobalInvocationID), vec4(val)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* The following code is the entire C++ source code for generating | |
* and rendering a procedural terraing as described here: | |
* https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch01.html | |
* | |
* I'm sorry if the code is messy or hard to understand but it's meant | |
* to be just a prototype and was not meant to be public. | |
* | |
* Third party libraries: | |
* - SGL (a personal project I'm working on, can be easily replaced by STL | |
* containers and GL math (glm)) | |
* - stb (image loading, only required if you want to load textures) | |
* - SDL (window, input and so on) | |
* | |
* The following code runs smooth in 1920x1080 on a Intel Core i7-4790k with | |
* an NVIDIA GTX 970 adapter. It runs ~40 fps in 1920x1080 on a Intel Core | |
* 8550u with an NVIDIA MX150. | |
* | |
* If your graphic adapter runs out of memory (storing all the geometry | |
* requires a good amount of memory, ~1GB) you can try to lower the number | |
* of availables vertex buffers. | |
*/ | |
// SGL includes | |
#include "coremin.h" | |
#include "math/math.h" | |
#include "gldrv/gldrv.h" | |
#define STB_IMAGE_IMPLEMENTATION | |
#include "stb_image.h" | |
#include <SDL.h> | |
#if SGL_BUILD_RELEASE | |
#define LOG(...) | |
#else | |
#define LOG(...) printf(__VA_ARGS__) | |
#endif | |
Malloc * gMalloc = nullptr; | |
/// Edge connect list, uploaded to GPU for marching | |
union | |
{ | |
int32 matrix[256][15]; | |
int32 array[256 * 15]; | |
} edgeConnectList = { | |
matrix : { | |
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1}, | |
{3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1}, | |
{3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1}, | |
{3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1}, | |
{9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1}, | |
{1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1}, | |
{9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1}, | |
{2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1}, | |
{8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1}, | |
{9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1}, | |
{4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1}, | |
{3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1}, | |
{1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1}, | |
{4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1}, | |
{4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1}, | |
{9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1}, | |
{1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1}, | |
{5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1}, | |
{2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1}, | |
{9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1}, | |
{0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1}, | |
{2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1}, | |
{10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1}, | |
{4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1}, | |
{5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1}, | |
{5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1}, | |
{9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1}, | |
{0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1}, | |
{1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1}, | |
{10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1}, | |
{8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1}, | |
{2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1}, | |
{7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1}, | |
{9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1}, | |
{2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1}, | |
{11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1}, | |
{9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1}, | |
{5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0}, | |
{11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0}, | |
{11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1}, | |
{1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1}, | |
{9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1}, | |
{5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1}, | |
{2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1}, | |
{0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1}, | |
{5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1}, | |
{6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1}, | |
{0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1}, | |
{3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1}, | |
{6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1}, | |
{5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1}, | |
{1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1}, | |
{10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1}, | |
{6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1}, | |
{1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1}, | |
{8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1}, | |
{7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9}, | |
{3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1}, | |
{5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1}, | |
{0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1}, | |
{9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6}, | |
{8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1}, | |
{5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11}, | |
{0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7}, | |
{6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1}, | |
{10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1}, | |
{10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1}, | |
{8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1}, | |
{1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1}, | |
{3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1}, | |
{0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1}, | |
{10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1}, | |
{0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1}, | |
{3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1}, | |
{6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1}, | |
{9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1}, | |
{8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1}, | |
{3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1}, | |
{6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1}, | |
{0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1}, | |
{10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1}, | |
{10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1}, | |
{1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1}, | |
{2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9}, | |
{7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1}, | |
{7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1}, | |
{2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7}, | |
{1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11}, | |
{11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1}, | |
{8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6}, | |
{0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1}, | |
{7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1}, | |
{10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1}, | |
{2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1}, | |
{6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1}, | |
{7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1}, | |
{2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1}, | |
{1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1}, | |
{10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1}, | |
{10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1}, | |
{0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1}, | |
{7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1}, | |
{6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1}, | |
{8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1}, | |
{9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1}, | |
{6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1}, | |
{1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1}, | |
{4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1}, | |
{10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3}, | |
{8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1}, | |
{0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1}, | |
{1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1}, | |
{8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1}, | |
{10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1}, | |
{4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3}, | |
{10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1}, | |
{5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1}, | |
{11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1}, | |
{9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1}, | |
{6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1}, | |
{7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1}, | |
{3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6}, | |
{7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1}, | |
{9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1}, | |
{3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1}, | |
{6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8}, | |
{9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1}, | |
{1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4}, | |
{4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10}, | |
{7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1}, | |
{6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1}, | |
{3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1}, | |
{0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1}, | |
{6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1}, | |
{1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1}, | |
{0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10}, | |
{11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5}, | |
{6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1}, | |
{5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1}, | |
{9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1}, | |
{1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8}, | |
{1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6}, | |
{10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1}, | |
{0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1}, | |
{5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1}, | |
{10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1}, | |
{11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1}, | |
{0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1}, | |
{9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1}, | |
{7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2}, | |
{2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1}, | |
{8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1}, | |
{9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1}, | |
{9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2}, | |
{1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1}, | |
{9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1}, | |
{9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1}, | |
{5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1}, | |
{0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1}, | |
{10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4}, | |
{2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1}, | |
{0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11}, | |
{0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5}, | |
{9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1}, | |
{5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1}, | |
{3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9}, | |
{5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1}, | |
{8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1}, | |
{0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1}, | |
{9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1}, | |
{0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1}, | |
{1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1}, | |
{3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4}, | |
{4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1}, | |
{9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3}, | |
{11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1}, | |
{11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1}, | |
{2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1}, | |
{9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7}, | |
{3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10}, | |
{1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1}, | |
{4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1}, | |
{4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1}, | |
{0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1}, | |
{3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1}, | |
{3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1}, | |
{0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1}, | |
{9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1}, | |
{1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, | |
{-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} | |
} | |
}; | |
uint32 zeroReg = 0; | |
/// Generation settings | |
const int32 blockSize = 2; | |
const int32 maxBlockResolution = 32; | |
/// Screen variables | |
point2 fboSize = point2(1920, 1080); | |
/// Time variables | |
float32 dt; | |
float32 currTime; | |
uint64 currTick; | |
uint64 prevTick; | |
float32 currFps; | |
vec2 fpsBounds; | |
/// Camera variables | |
vec3 cameraLocation; | |
vec3 cameraVelocity; | |
quat cameraRotation; | |
mat4 cameraTransform; | |
mat4 projectionMatrix; | |
class FileReader | |
{ | |
protected: | |
FILE * fp; | |
void * buffer; | |
uint64 len; | |
public: | |
FileReader(const char * filename) : | |
fp(fopen(filename, "r")) | |
{ | |
if (fp) | |
{ | |
// Get file length | |
fseek(fp, 0, SEEK_END); | |
len = ftell(fp); | |
fseek(fp, 0, SEEK_SET); | |
// Allocate buffer | |
if (posix_memalign((void**)&buffer, sizeof(void*), len + 1) == 0) | |
{ | |
get<char>()[len] = 0; | |
fread(buffer, 1, len, fp); | |
} | |
} | |
} | |
~FileReader() | |
{ | |
if (fp) fclose(fp); | |
if (buffer) free(buffer); | |
} | |
FORCE_INLINE bool isValid() const { return fp && buffer && len > 0; } | |
template<typename T> | |
FORCE_INLINE T * get() { return reinterpret_cast<T*>(buffer); } | |
}; | |
class ShaderProgram | |
{ | |
protected: | |
/// Program name | |
uint32 name; | |
/// Uniforms map | |
Map<String, uint32> uniforms; | |
public: | |
/// Default constructor | |
FORCE_INLINE ShaderProgram() : | |
name(glCreateProgram()), | |
uniforms{} {} | |
/// Set active | |
FORCE_INLINE void bind() | |
{ | |
glUseProgram(name); | |
} | |
public: | |
/// Set shader | |
FORCE_INLINE void setShader(uint32 shader) | |
{ | |
glAttachShader(name, shader); | |
} | |
/// Link program | |
FORCE_INLINE void link() | |
{ | |
glLinkProgram(name); | |
} | |
/// Get program status | |
FORCE_INLINE int32 getStatus(uint32 iv = GL_LINK_STATUS) | |
{ | |
int32 status = 0; | |
glGetProgramiv(name, iv, &status); | |
return status; | |
} | |
protected: | |
/// Set uniform | |
template<typename T, typename Lambda> | |
void setUniform_internal(const String & key, T val, Lambda glFun) | |
{ | |
auto it = uniforms.find(key); | |
if (it != uniforms.end()) | |
glFun(it->second, val); | |
else | |
{ | |
uint32 slot = glGetUniformLocation(name, *key); | |
glFun(slot, val); | |
// Cache in map | |
uniforms.insert(key, slot); | |
} | |
} | |
public: | |
/// Set uniform | |
template<typename T> | |
void setUniform(const String & key, T val); | |
}; | |
template<> | |
FORCE_INLINE void ShaderProgram::setUniform<float32>(const String & key, float32 val) | |
{ | |
setUniform_internal(key, val, glUniform1f); | |
} | |
template<> | |
FORCE_INLINE void ShaderProgram::setUniform<int32>(const String & key, int32 val) | |
{ | |
setUniform_internal(key, val, glUniform1i); | |
} | |
template<> | |
FORCE_INLINE void ShaderProgram::setUniform<uint32>(const String & key, uint32 val) | |
{ | |
setUniform_internal(key, val, glUniform1ui); | |
} | |
template<> | |
FORCE_INLINE void ShaderProgram::setUniform<Vec2<int32>>(const String & key, Vec2<int32> val) | |
{ | |
setUniform_internal(key, val, [](uint32 slot, Vec2<int32> val) { | |
glUniform2iv(slot, 1, val.buffer); | |
}); | |
} | |
template<> | |
FORCE_INLINE void ShaderProgram::setUniform<const Vec3<float32, true>&>(const String & key, const Vec3<float32, true> & val) | |
{ | |
setUniform_internal(key, val, [](uint32 slot, const Vec3<float32, true> & val) { | |
glUniform3fv(slot, 1, val.buffer); | |
}); | |
} | |
template<> | |
FORCE_INLINE void ShaderProgram::setUniform<const Vec3<float32, false>&>(const String & key, const Vec3<float32, false> & val) | |
{ | |
setUniform_internal(key, val, [](uint32 slot, const Vec3<float32, false> & val) { | |
glUniform3fv(slot, 1, val.buffer); | |
}); | |
} | |
template<> | |
FORCE_INLINE void ShaderProgram::setUniform<const mat4&>(const String & key, const mat4 & val) | |
{ | |
setUniform_internal(key, val, [](uint32 slot, const mat4 & val) { | |
glUniformMatrix4fv(slot, 1, GL_TRUE, val.array); | |
}); | |
} | |
void setupPerlin() | |
{ | |
int32 perms[0x100]; | |
Vec3<float32, false> grads[0x100]; | |
const float32 freq = 2.f * M_PI / (float32)0x100; | |
// Fill uniformly | |
for (uint32 i = 0; i < 0x100; ++i) | |
perms[i] = i; | |
// Shuffle | |
for (uint32 i = 0, range = 0x100; i < 0x100; ++i, --range) | |
{ | |
uint8 k = rand() % range + i; | |
swap(perms[i], perms[k]); | |
} | |
// Generate gradients | |
for (uint32 i = 0; i < 0x100; ++i) | |
grads[i] = Vec3<float32, false>( | |
Math::cos(perms[i] * freq), | |
Math::cos(perms[perms[i]] * freq), | |
Math::sin(perms[i] * freq) | |
).normalize(); | |
// Upload to GPU | |
sizet offset; | |
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, (offset = 0x100 * sizeof(perms[0])), perms); | |
glBufferSubData(GL_SHADER_STORAGE_BUFFER, offset, 0x100 * sizeof(grads[0]), grads); | |
}; | |
int32 main() | |
{ | |
/** | |
* Some nice seed values: | |
* | |
* -12359012 | |
* 1230042 | |
* 85642 | |
*/ | |
Memory::createGMalloc(); | |
srand(clock()); | |
Map<uint32, float32> keys; | |
Map<String, float32> axes; | |
axes["mouseX"] = 0.f; | |
axes["mouseY"] = 0.f; | |
initOpenGL(); | |
SDL_Window * window = SDL_CreateWindow("light", 0, 0, fboSize.x, fboSize.y, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS); | |
SDL_GLContext context = SDL_GL_CreateContext(window); | |
SDL_GL_SetSwapInterval(1); | |
SDL_SetRelativeMouseMode(SDL_TRUE); | |
// Init time | |
dt = 0.f; | |
currTime = 0.f; | |
currTick = prevTick = SDL_GetPerformanceCounter(); | |
fpsBounds = vec2(FLT_MAX, 0.f); | |
glEnable(GL_DEPTH_TEST); | |
glEnable(GL_CULL_FACE); | |
glFrontFace(GL_CW); | |
glPointSize(2); | |
////////////////////////////////////////////////// | |
// Program setup | |
////////////////////////////////////////////////// | |
// Setup perlin noise tables | |
uint32 perlinTables[2]; | |
const sizet perlinTableSize = 0x100 * (sizeof(int32) + sizeof(Vec3<float32, false>)); | |
glGenBuffers(2, perlinTables); | |
// Terrain perlin noise | |
glBindBuffer(GL_SHADER_STORAGE_BUFFER, perlinTables[0]); | |
glBufferData(GL_SHADER_STORAGE_BUFFER, perlinTableSize, nullptr, GL_STATIC_DRAW); | |
setupPerlin(); | |
// Fog perlin noise | |
glBindBuffer(GL_SHADER_STORAGE_BUFFER, perlinTables[1]); | |
glBufferData(GL_SHADER_STORAGE_BUFFER, perlinTableSize, nullptr, GL_STATIC_DRAW); | |
setupPerlin(); | |
// You can also go nuts and try some other noises | |
// Setup marching cube tables | |
uint32 marchingTable; | |
/* glGenTextures(1, &marchingTable); | |
glBindTexture(GL_TEXTURE_2D, marchingTable); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8I, 256, 5, 0, GL_RGB_INTEGER, GL_INT, edgeConnectList.array); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); */ | |
glGenBuffers(1, &marchingTable); | |
glBindBuffer(GL_SHADER_STORAGE_BUFFER, marchingTable); | |
glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(edgeConnectList.array), edgeConnectList.array, GL_STATIC_DRAW); | |
ShaderProgram fogProg; | |
{ | |
uint32 shader = glCreateShader(GL_COMPUTE_SHADER); | |
FileReader source = "src/light/shaders/volume/gen.comp"; | |
const char * buffer = source.get<char>(); | |
glShaderSource(shader, 1, &buffer, nullptr); | |
glCompileShader(shader); | |
fogProg.setShader(shader); | |
int32 status = 0; | |
glGetShaderiv(shader, GL_COMPILE_STATUS, &status); | |
if (status == GL_FALSE) printf("shader not compiled\n"); | |
} | |
fogProg.link(); | |
fogProg.bind(); | |
if (fogProg.getStatus() == 0) printf("volume generation program not linked correctly\n"); | |
ShaderProgram genProg; | |
{ | |
uint32 shader = glCreateShader(GL_COMPUTE_SHADER); | |
FileReader source = "src/light/shaders/march/gen.comp"; | |
const char * buffer = source.get<char>(); | |
glShaderSource(shader, 1, &buffer, nullptr); | |
glCompileShader(shader); | |
genProg.setShader(shader); | |
int32 status = 0; | |
glGetShaderiv(shader, GL_COMPILE_STATUS, &status); | |
if (status == GL_FALSE) printf("shader not compiled\n"); | |
} | |
genProg.link(); | |
genProg.bind(); | |
if (genProg.getStatus() == 0) printf("generation program not linked correctly\n"); | |
ShaderProgram marchProg; | |
{ | |
uint32 shader = glCreateShader(GL_COMPUTE_SHADER); | |
FileReader source = "src/light/shaders/march/march.comp"; | |
const char * buffer = source.get<char>(); | |
glShaderSource(shader, 1, &buffer, nullptr); | |
glCompileShader(shader); | |
marchProg.setShader(shader); | |
int32 status = 0; | |
glGetShaderiv(shader, GL_COMPILE_STATUS, &status); | |
if (status == GL_FALSE) printf("shader not compiled\n"); | |
} | |
marchProg.link(); | |
marchProg.bind(); | |
if (genProg.getStatus() == 0) printf("marching program not linked correctly\n"); | |
ShaderProgram drawProg; | |
{ | |
uint32 shader = glCreateShader(GL_VERTEX_SHADER); | |
FileReader source = "src/light/shaders/deferred/.vert"; | |
const char * buffer = source.get<char>(); | |
glShaderSource(shader, 1, &buffer, nullptr); | |
glCompileShader(shader); | |
drawProg.setShader(shader); | |
} | |
{ | |
uint32 shader = glCreateShader(GL_FRAGMENT_SHADER); | |
FileReader source = "src/light/shaders/deferred/.frag"; | |
const char * buffer = source.get<char>(); | |
glShaderSource(shader, 1, &buffer, nullptr); | |
glCompileShader(shader); | |
drawProg.setShader(shader); | |
} | |
drawProg.link(); | |
drawProg.bind(); | |
if (drawProg.getStatus() == 0) printf("drawing program not linked correctly\n"); | |
const char * terrainImages[] = { | |
"assets/rock2_diffuse.jpg", | |
"assets/sand_diffuse.jpg", | |
"assets/rock2_diffuse.jpg" | |
}; | |
uvec3 terrainLayers; | |
glGenTextures(3, terrainLayers.buffer); | |
for (uint32 i = 0; i < 3; ++i) | |
{ | |
struct | |
{ | |
int32 width, height, channels; | |
ubyte * data; | |
} img; | |
img.data = stbi_load(terrainImages[i], &img.width, &img.height, &img.channels, 0); | |
glBindTexture(GL_TEXTURE_2D, terrainLayers[i]); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img.width, img.height, 0, GL_RGB, GL_UNSIGNED_BYTE, img.data); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glGenerateMipmap(GL_TEXTURE_2D); | |
} | |
ShaderProgram renderProg; | |
{ | |
uint32 shader = glCreateShader(GL_COMPUTE_SHADER); | |
FileReader source = "src/light/shaders/deferred/.comp"; | |
const char * buffer = source.get<char>(); | |
glShaderSource(shader, 1, &buffer, nullptr); | |
glCompileShader(shader); | |
renderProg.setShader(shader); | |
int32 status = 0; | |
glGetShaderiv(shader, GL_COMPILE_STATUS, &status); | |
if (status == GL_FALSE) printf("shader not compiled\n"); | |
} | |
renderProg.link(); | |
renderProg.bind(); | |
if (renderProg.getStatus() == 0) printf("render program not linked correctly\n"); | |
////////////////////////////////////////////////// | |
// Framebuffer setup | |
////////////////////////////////////////////////// | |
enum DeferredRenderTargets | |
{ | |
RT_POSITION = 0, | |
RT_NORMAL, | |
RT_COLOR, | |
RT_DEPTH, | |
RT_NUM_RENDER_TARGETS | |
}; | |
uint32 fbo; | |
glGenFramebuffers(1, &fbo); | |
glBindFramebuffer(GL_FRAMEBUFFER, fbo); | |
uint32 renderTargets[RT_NUM_RENDER_TARGETS]; | |
uint32 backbuffer; | |
glGenTextures(RT_NUM_RENDER_TARGETS, renderTargets); | |
glGenTextures(1, &backbuffer); | |
/// Position and normal | |
for (uint32 i = RT_POSITION; i <= RT_NORMAL; ++i) | |
{ | |
glBindTexture(GL_TEXTURE_2D, renderTargets[i]); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB16F, fboSize.x, fboSize.y, 0, GL_RGB, GL_FLOAT, nullptr); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, renderTargets[i], 0); | |
} | |
/// Color | |
glBindTexture(GL_TEXTURE_2D, renderTargets[RT_COLOR]); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fboSize.x, fboSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + RT_COLOR, GL_TEXTURE_2D, renderTargets[RT_COLOR], 0); | |
/// Depth | |
glBindTexture(GL_TEXTURE_2D, renderTargets[RT_DEPTH]); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, fboSize.x, fboSize.y, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, renderTargets[RT_DEPTH], 0); | |
/// Backbuffer | |
glBindTexture(GL_TEXTURE_2D, backbuffer); | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, fboSize.x, fboSize.y, 0, GL_RGBA, GL_FLOAT, nullptr); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, GL_TEXTURE_2D, backbuffer, 0); | |
uint32 drawBuffers[RT_COLOR - RT_POSITION + 1]; for (uint32 i = RT_POSITION; i <= RT_COLOR; ++i) drawBuffers[i] = GL_COLOR_ATTACHMENT0 + i; | |
glDrawBuffers(RT_COLOR - RT_POSITION + 1, drawBuffers); | |
{ | |
uint32 status = glCheckFramebufferStatus(GL_FRAMEBUFFER); | |
if (status != GL_FRAMEBUFFER_COMPLETE) | |
printf("Framebuffer error %x:%u\n", status, status); | |
} | |
// Reset default framebuffer | |
glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
////////////////////////////////////////////////// | |
// Buffer setup | |
////////////////////////////////////////////////// | |
// Note that vec3 has three components but the size | |
// of a vec4. This is very important when working with | |
// SSBs (Shader Storage Buffer), because depending on | |
// the layout you use you may need to align all the | |
// members to the size of a vec4 (like I did for the | |
// first unsigned integer) | |
struct ChunkData | |
{ | |
/// Vertex index, zero indicates no geometry generated | |
uint32 index; | |
/// Padding | |
uint32 pad0[3]; | |
/// Chunk origin | |
vec3 origin; | |
struct | |
{ | |
/// Vertex position | |
vec3 pos; | |
/// Vertex normal | |
vec3 norm; | |
} vertices[maxBlockResolution * maxBlockResolution * maxBlockResolution * 5]; | |
} chunk; | |
uint32 vao; | |
glGenVertexArrays(1, &vao); | |
glBindVertexArray(vao); | |
struct IVec3Compare | |
{ | |
FORCE_INLINE int32 operator()(const vec3 & a, const vec3 & b) const | |
{ | |
const Compare compare; | |
return compare(a.x, b.x) * 4 + compare(a.y, b.y) * 2 + compare(a.z, b.z); | |
} | |
}; | |
struct Chunk | |
{ | |
bool bActive; | |
uint32 vbo; | |
uint32 numVertices; | |
}; | |
Map<vec3, Chunk, IVec3Compare> chunks; | |
LinkedList<Chunk*> activeChunks; | |
const uint32 numVbos = 320; // Lower this if the graphic adapter runs out of memory | |
Queue<uint32> vbos; | |
{ | |
uint32 buffers[numVbos]; | |
glGenBuffers(numVbos, buffers); | |
for (uint32 i = 0; i < numVbos; ++i) | |
{ | |
glBindBuffer(GL_ARRAY_BUFFER, buffers[i]); | |
glBufferStorage(GL_ARRAY_BUFFER, sizeof(ChunkData), &chunk, GL_DYNAMIC_STORAGE_BIT | GL_MAP_READ_BIT); | |
vbos.push(buffers[i]); | |
} | |
} | |
/// Vertex attrib format | |
glEnableVertexAttribArray(0); | |
glEnableVertexAttribArray(1); | |
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0); | |
glVertexAttribFormat(1, 3, GL_FLOAT, GL_FALSE, 16); | |
// Terrain shared density map | |
uint32 volumeData; | |
const ivec3 volumeRes(maxBlockResolution + 4); | |
glGenTextures(1, &volumeData); | |
glBindTexture(GL_TEXTURE_3D, volumeData); | |
glTexImage3D(GL_TEXTURE_3D, 0, GL_R32F, volumeRes.x, volumeRes.y, volumeRes.z, 0, GL_RED, GL_FLOAT, nullptr); | |
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
////////////////////////////////////////////////// | |
// Fog volume data generation | |
////////////////////////////////////////////////// | |
uint32 fogData; | |
glGenTextures(1, &fogData); | |
glBindTexture(GL_TEXTURE_3D, fogData); | |
glTexImage3D(GL_TEXTURE_3D, 0, GL_R32F, 256, 256, 256, 0, GL_RED, GL_FLOAT, nullptr); | |
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
// Generate fog data | |
fogProg.bind(); | |
glBindImageTexture(0, fogData, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); | |
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, perlinTables[1]); | |
glDispatchCompute(256 / 8, 256 / 8, 256 / 8); | |
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); | |
////////////////////////////////////////////////// | |
// Camera setup | |
////////////////////////////////////////////////// | |
projectionMatrix = mat4::glProjection(M_PI_2 * 1.1f, 0.1f); | |
cameraLocation = vec3(0.f, 1.f, 0.f); | |
cameraVelocity = vec3::zero; | |
cameraRotation = quat(0.f, vec3::up); | |
////////////////////////////////////////////////// | |
// Main loop | |
////////////////////////////////////////////////// | |
bool bRunning = true; | |
while (bRunning) | |
{ | |
// Update time variables | |
currTime += (dt = ((currTick = SDL_GetPerformanceCounter()) - prevTick) / (float32)SDL_GetPerformanceFrequency()); | |
prevTick = currTick; | |
currFps = 1.f / dt; | |
printf("%.4f s -> %f fps [%.4f : %.4f]\n", dt, currFps, fpsBounds[0], fpsBounds[1]); | |
if (currFps > 30.f) | |
{ | |
fpsBounds[0] = Math::min(fpsBounds[0], currFps); | |
fpsBounds[1] = Math::max(fpsBounds[1], currFps); | |
} | |
SDL_Event e; | |
while (SDL_PollEvent(&e)) | |
{ | |
switch (e.type) | |
{ | |
case SDL_QUIT: | |
bRunning = false; | |
break; | |
case SDL_KEYDOWN: | |
keys[e.key.keysym.sym] = 1.f; | |
bRunning &= e.key.keysym.sym != SDLK_ESCAPE; | |
if (e.key.keysym.sym == SDLK_1) | |
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); | |
else if (e.key.keysym.sym == SDLK_2) | |
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); | |
else if (e.key.keysym.sym == SDLK_3) | |
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT); | |
break; | |
case SDL_KEYUP: | |
keys[e.key.keysym.sym] = 0.f; | |
break; | |
case SDL_MOUSEMOTION: | |
axes["mouseX"] = e.motion.xrel; | |
axes["mouseY"] = e.motion.yrel; | |
break; | |
} | |
} | |
////////////////////////////////////////////////// | |
// Camera position and rotation | |
////////////////////////////////////////////////// | |
const float32 cameraSpeed = 3.f; | |
const float32 cameraBrake = 3.4f; | |
vec3 cameraAcceleration = cameraRotation * vec3( | |
keys[SDLK_d] - keys[SDLK_a], | |
keys[SDLK_SPACE] - keys[SDLK_LCTRL], | |
keys[SDLK_w] - keys[SDLK_s] | |
) * cameraSpeed - (cameraVelocity) * cameraBrake; | |
cameraVelocity += cameraAcceleration * dt; | |
cameraLocation += cameraVelocity * dt; | |
cameraRotation | |
= quat((keys[SDLK_RIGHT] - keys[SDLK_LEFT] + axes["mouseX"] * 0.5f) * dt, vec3::up) | |
* quat((keys[SDLK_DOWN] - keys[SDLK_UP] + axes["mouseY"] * 0.5f) * dt, cameraRotation.right()) | |
* cameraRotation; | |
cameraTransform = mat4::rotation(!cameraRotation) * mat4::translation(-cameraLocation); | |
const mat4 viewMatrix = projectionMatrix * cameraTransform; | |
const vec3 cameraDir = cameraRotation.forward(); | |
cameraDir.print(); | |
////////////////////////////////////////////////// | |
// Draw | |
////////////////////////////////////////////////// | |
const ivec3 radius(3); // 7x7x7 area | |
uint32 numVertices = 0; | |
////////////////////////////////////////////////// | |
// Begin chunk rendering | |
////////////////////////////////////////////////// | |
drawProg.bind(); | |
drawProg.setUniform<const mat4&>("modelMatrix", mat4::eye(1.f)); | |
drawProg.setUniform<const mat4&>("viewMatrix", viewMatrix); | |
drawProg.setUniform<float32>("currTime", currTime); | |
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); | |
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); | |
const uint32 maxUpdatesPerFrame = 2; | |
uint32 currNumUpdates = 0; | |
// Reset active state | |
for (auto chunkRef : activeChunks) | |
chunkRef->bActive = false; | |
auto oldActiveChunks = activeChunks; | |
activeChunks.empty(); | |
for (int32 i = -radius.x; i <= radius.x; ++i) | |
{ | |
for (int32 j = -radius.z; j <= radius.z; ++j) | |
{ | |
for (int32 h = -radius.y; h <= radius.y; ++h) | |
{ | |
const ivec3 voxelIndex = ivec3(cameraLocation / (float32)blockSize) * blockSize + ivec3(i, h, j) * blockSize; | |
const vec3 voxelOrigin = (const vec3&)voxelIndex; | |
const vec3 voxelOffset = vec3(i, h, j) + 0.5f; | |
if (true/* voxelOffset.getSquaredSize() < 4.f || (voxelOffset.getNormal() & cameraDir) > 0.2f */) | |
{ | |
bool bUpdate = false; | |
auto voxelIt = chunks.find(voxelIndex); | |
// Draw what we have | |
if (voxelIt != chunks.end()) | |
{ | |
Chunk & chunk = voxelIt->second; | |
////////////////////////////////////////////////// | |
// Draw geometry | |
////////////////////////////////////////////////// | |
if (chunk.vbo >= 0 && chunk.numVertices > 0) | |
{ | |
drawProg.bind(); | |
glBindVertexBuffer(0, chunk.vbo, 32, 32); | |
glVertexAttribBinding(0, 0); | |
glVertexAttribBinding(1, 0); | |
for (uint32 i = 0; i < 3; ++i) | |
{ | |
glActiveTexture(GL_TEXTURE0 + i); | |
glBindTexture(GL_TEXTURE_2D, terrainLayers[i]); | |
} | |
glDrawArrays(GL_TRIANGLES, 0, chunk.numVertices); | |
numVertices += chunk.numVertices; | |
chunk.bActive = true; | |
activeChunks.push(&chunk); | |
} | |
else if (chunk.numVertices > 0) | |
bUpdate = true; | |
} | |
else | |
bUpdate = true; | |
////////////////////////////////////////////////// | |
// Generate new geometry | |
////////////////////////////////////////////////// | |
if (bUpdate && currNumUpdates < maxUpdatesPerFrame) | |
{ | |
uint32 vbo; | |
if (vbos.pop(vbo)) | |
{ | |
// Get new or existing chunk | |
Chunk & chunk = chunks[voxelIndex]; | |
// Generate data, render on next frame | |
genProg.bind(); | |
genProg.setUniform<float32>("blockSize", (float32)blockSize); | |
genProg.setUniform<int32>("blockResolution", maxBlockResolution); | |
genProg.setUniform<const vec3&>("origin", voxelOrigin); | |
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, perlinTables[0]); | |
glBindImageTexture(0, volumeData, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_R32F); | |
glDispatchCompute(1, maxBlockResolution + 4, 1); | |
glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); | |
// Generate geometry | |
marchProg.bind(); | |
marchProg.setUniform<float32>("blockSize", (float32)blockSize); | |
marchProg.setUniform<int32>("blockResolution", maxBlockResolution); | |
marchProg.setUniform<const vec3&>("origin", voxelOrigin); | |
glNamedBufferSubData(vbo, 0, sizeof(uint32), &zeroReg); | |
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, vbo); | |
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, marchingTable); | |
glActiveTexture(GL_TEXTURE0); | |
glBindTexture(GL_TEXTURE_3D, volumeData); | |
glDispatchCompute(1, maxBlockResolution, 1); | |
glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); | |
ChunkData * chunkData = reinterpret_cast<ChunkData*>(glMapNamedBufferRange(vbo, 0, 64, GL_MAP_READ_BIT)); | |
chunk.numVertices = chunkData->index; | |
chunk.vbo = vbo; | |
chunk.bActive = chunk.numVertices > 0; | |
glUnmapNamedBuffer(vbo); | |
activeChunks.push(&chunk); | |
++currNumUpdates; | |
} | |
} | |
} | |
} | |
} | |
} | |
// Release unused buffers | |
for (auto chunkRef : oldActiveChunks) | |
if (!chunkRef->bActive && chunkRef->vbo > 0) | |
{ | |
vbos.push(chunkRef->vbo); | |
chunkRef->vbo = 0; | |
} | |
#if SGL_BUILD_DEBUG | |
printf("num vertices: %u\n", numVertices); | |
printf("num free buffers: %llu\n", vbos.getCount()); | |
printf("num active chunks: %u\n", activeChunks.getCount()); | |
printf("generated chunks: %llu\n", chunks.getCount()); | |
#endif | |
////////////////////////////////////////////////// | |
// End Chunk rendering | |
////////////////////////////////////////////////// | |
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); | |
////////////////////////////////////////////////// | |
// Fog rendering and deferred shading | |
////////////////////////////////////////////////// | |
renderProg.bind(); | |
renderProg.setUniform<float32>("time", currTime); | |
renderProg.setUniform<ivec2>("fboSize", fboSize); | |
renderProg.setUniform<float32>("samplingStep", 0.5f); | |
renderProg.setUniform<const mat4&>("viewMatrix", viewMatrix); | |
renderProg.setUniform<const vec3&>("cameraLocation", cameraLocation); | |
for (uint32 i = RT_POSITION; i <= RT_COLOR; ++i) | |
{ | |
glActiveTexture(GL_TEXTURE0 + i); | |
glBindTexture(GL_TEXTURE_2D, renderTargets[i]); | |
} | |
glActiveTexture(GL_TEXTURE0 + RT_DEPTH); | |
glBindTexture(GL_TEXTURE_2D, renderTargets[RT_DEPTH]); | |
glActiveTexture(GL_TEXTURE10); | |
glBindTexture(GL_TEXTURE_3D, fogData); | |
glBindImageTexture(0, backbuffer, 0, GL_FALSE, 0, GL_WRITE_ONLY, GL_RGBA32F); | |
glDispatchCompute(fboSize.x / 48, fboSize.y / 27, 1); | |
glMemoryBarrier(GL_ALL_BARRIER_BITS); | |
////////////////////////////////////////////////// | |
// Framebuffer composition | |
////////////////////////////////////////////////// | |
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); | |
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo); | |
#if SGL_BUILD_DEBUG | |
glReadBuffer(drawBuffers[RT_POSITION]); | |
glBlitFramebuffer(0, 0, fboSize.x, fboSize.y, 0, fboSize.y / 2, fboSize.x / 2, fboSize.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); | |
glReadBuffer(drawBuffers[RT_NORMAL]); | |
glBlitFramebuffer(0, 0, fboSize.x, fboSize.y, fboSize.x / 2, fboSize.y / 2, fboSize.x, fboSize.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); | |
glReadBuffer(drawBuffers[RT_COLOR]); | |
glBlitFramebuffer(0, 0, fboSize.x, fboSize.y, 0, 0, fboSize.x / 2, fboSize.y / 2, GL_COLOR_BUFFER_BIT, GL_NEAREST); | |
glReadBuffer(GL_COLOR_ATTACHMENT4); | |
glBlitFramebuffer(0, 0, fboSize.x, fboSize.y, fboSize.x / 2, 0, fboSize.x, fboSize.y / 2, GL_COLOR_BUFFER_BIT, GL_NEAREST); | |
#else | |
glReadBuffer(GL_COLOR_ATTACHMENT4); | |
glBlitFramebuffer(0, 0, fboSize.x, fboSize.y, 0, 0, fboSize.x, fboSize.y, GL_COLOR_BUFFER_BIT, GL_NEAREST); | |
#endif | |
glBindFramebuffer(GL_FRAMEBUFFER, 0); | |
// Consume mouse input | |
axes["mouseX"] = 0.f; | |
axes["mouseY"] = 0.f; | |
SDL_GL_SwapWindow(window); | |
} | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////////////////////////////////////////// | |
// This compute shader generates the density | |
// map for a chunk and stores them in a | |
// 3D texture (borders are required to | |
// compute smooth normals | |
////////////////////////////////////////////////// | |
#version 450 core | |
/// Time variables | |
uniform float currTime; | |
/// Scalara size of a block | |
uniform float blockSize = 1.f; | |
/// Block resolution (i.e. number of voxels per side) | |
uniform int blockResolution = 32; | |
struct PerlinData | |
{ | |
/// Permutation table | |
int perms[256]; | |
/// Gradients table | |
vec3 grads[256]; | |
}; | |
/// Perlin data buffer | |
layout(binding = 0, std430) buffer PerlinBuffer | |
{ | |
PerlinData tables[]; | |
}; | |
/// Buffer used to store density values | |
layout(binding = 0, r32f) writeonly uniform image3D densityMap; | |
/// Cube origin | |
uniform vec3 origin; | |
/// Vertices of a [<0,0,0>, <1,1,1>] cube | |
const vec3 cubeCorners[] = { | |
vec3(0.f, 0.f, 0.f), | |
vec3(1.f, 0.f, 0.f), | |
vec3(0.f, 1.f, 0.f), | |
vec3(1.f, 1.f, 0.f), | |
vec3(0.f, 0.f, 1.f), | |
vec3(1.f, 0.f, 1.f), | |
vec3(0.f, 1.f, 1.f), | |
vec3(1.f, 1.f, 1.f), | |
}; | |
/// Compute dot between position and gradient | |
/// @{ | |
float grad(int k, vec3 p, ivec3 i, ivec3 period) | |
{ | |
const vec3 grad = tables[k].grads[tables[k].perms[(tables[k].perms[(tables[k].perms[i.x % period.x] + i.y) % period.y] + i.z) % period.z]]; | |
return dot(p, grad); | |
} | |
float grad(vec3 p, ivec3 i, ivec3 period) | |
{ | |
return grad(0, p, i, period); | |
} | |
/// @} | |
/// Sample perlin noise | |
float perlinSample(vec3 p, ivec3 period) | |
{ | |
period = min(period, 256); | |
vec3 i = floor(p); | |
vec3 t = fract(p); | |
// Soft curve | |
// @ref https://www.google.com/search?ei=tvSgXMDCGMvisAeL7bawAQ&q=x+*+x+*+%283+-+2+*+x%29&oq=x+*+x+*+%283+-+2+*+x%29&gs_l=psy-ab.3..0i13i30l2j0i22i30l8.4020.9555..9980...0.0..0.119.1704.14j5......0....1..gws-wiz.......0i71j35i39j0j0i67j0i22i10i30.e3qAa8TpAUc | |
vec3 w = t * t * (3.f - 2.f * t); | |
return mix( | |
mix( | |
mix( | |
grad(t - cubeCorners[0], ivec3(i + cubeCorners[0]), period), | |
grad(t - cubeCorners[1], ivec3(i + cubeCorners[1]), period), | |
w.x | |
), | |
mix( | |
grad(t - cubeCorners[2], ivec3(i + cubeCorners[2]), period), | |
grad(t - cubeCorners[3], ivec3(i + cubeCorners[3]), period), | |
w.x | |
), | |
w.y | |
), | |
mix( | |
mix( | |
grad(t - cubeCorners[4], ivec3(i + cubeCorners[4]), period), | |
grad(t - cubeCorners[5], ivec3(i + cubeCorners[5]), period), | |
w.x | |
), | |
mix( | |
grad(t - cubeCorners[6], ivec3(i + cubeCorners[6]), period), | |
grad(t - cubeCorners[7], ivec3(i + cubeCorners[7]), period), | |
w.x | |
), | |
w.y | |
), | |
w.z | |
); | |
} | |
float octaves(vec3 p, ivec3 period, uint numOctaves) | |
{ | |
float f = 0.5f; | |
float s = 1.f; | |
float v = 0.f; | |
for (uint i = 0; i < numOctaves; ++i, f *= 2.f, s *= 0.5f) | |
v += perlinSample(p * f, period) * s; | |
return v; | |
} | |
float monoOctaves(vec3 p, ivec3 period, uint numOctaves) | |
{ | |
float f = 0.05f; | |
float s = 1.f; | |
float v = 0.f; | |
for (uint i = 0; i < numOctaves; ++i, f *= 2.f, s *= 0.25f) | |
v += abs(perlinSample(p * f, period)) * s; | |
return v; | |
} | |
layout(local_size_x = 36, local_size_y = 1, local_size_z = 36) in; | |
void main() | |
{ | |
// Write to density buffer | |
const ivec3 voxelIndex = ivec3(gl_GlobalInvocationID); | |
if (voxelIndex.x < blockResolution + 4 && voxelIndex.z < blockResolution + 4) | |
{ | |
const vec3 p = origin + vec3(voxelIndex - 2) / (blockResolution / blockSize); | |
const vec3 pw = p + perlinSample(p, ivec3(256)) * 0.f; | |
float rad = 8.f; | |
//float val = rad - length(p - vec3(0.f, -rad, 0.f)); // Planet | |
//float val = rad - length(p.xz); // Column | |
//float val = 0.f; // Caves | |
float val = -pw.y; // Plane | |
val += octaves(pw, ivec3(256), 11) * 6.f; | |
//val -= clamp((-0.5f + pw.y) * 3.f, 0.f, 1.f) * 8.f; // Terraces | |
imageStore(densityMap, voxelIndex, vec4(val)); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
////////////////////////////////////////////////// | |
// This is an implementation of the marching cube | |
// algorithm on the GPU. | |
// This CS is dispatch each time we are required | |
// to generate the geometry for a new voxel | |
////////////////////////////////////////////////// | |
#version 450 core | |
struct Vertex | |
{ | |
// Vertex position | |
vec3 pos; | |
// Vertex normal | |
vec3 norm; | |
}; | |
/// Block size | |
uniform float blockSize = 1.f; | |
/// Block resolution | |
uniform int blockResolution = 32; | |
/// A vertex buffer | |
layout(binding = 0, std430) buffer VertexBuffer | |
{ | |
/// Currently writeable buffer index | |
uint index; | |
/// Chunk origin | |
vec3 origin; | |
/// Array of geometry vertices | |
Vertex vertices[]; | |
} chunk; | |
/// Marching vertices buffer | |
layout(binding = 1, std430) buffer MarchingTable | |
{ | |
int edgesTable[256 * 15]; | |
}; | |
//layout(binding = 0) uniform isampler2D edgesTable; | |
/// Buffers used to read density values | |
//layout(binding = 0, r32f) readonly uniform image3D densityBuffer; | |
layout(binding = 0) uniform sampler3D densityMap; | |
/// Cube origin | |
uniform vec3 origin; | |
/// Vertices of a [<0,0,0>, <1,1,1>] cube | |
const ivec3 cubeCorners[] = { | |
ivec3(0, 0, 0), | |
ivec3(0, 1, 0), | |
ivec3(1, 1, 0), | |
ivec3(1, 0, 0), | |
ivec3(0, 0, 1), | |
ivec3(0, 1, 1), | |
ivec3(1, 1, 1), | |
ivec3(1, 0, 1), | |
}; | |
/// Generate geometry for voxel | |
void march(ivec3 voxelIndex) | |
{ | |
// Voxel offset position, [0.f - 31.f] | |
const vec3 voxelOffset = vec3(voxelIndex); | |
// Read density values | |
float densities[8]; | |
for (uint i = 0; i < 8; ++i) | |
densities[i] = texelFetch(densityMap, (voxelIndex + 2) + cubeCorners[i], 0).r; | |
// Compute marching case | |
uint marchingCase | |
= (uint(densities[0] > 0.f) << 0) | |
| (uint(densities[1] > 0.f) << 1) | |
| (uint(densities[2] > 0.f) << 2) | |
| (uint(densities[3] > 0.f) << 3) | |
| (uint(densities[4] > 0.f) << 4) | |
| (uint(densities[5] > 0.f) << 5) | |
| (uint(densities[6] > 0.f) << 6) | |
| (uint(densities[7] > 0.f) << 7); | |
// No geometry here | |
if (marchingCase == 0x0 || marchingCase == 0xff) return; | |
// Pre-compute edge vertices | |
vec3 vertPerEdge[12] = { | |
voxelOffset + mix(cubeCorners[0], cubeCorners[1], (-densities[0]) / (densities[1] - densities[0])), | |
voxelOffset + mix(cubeCorners[1], cubeCorners[2], (-densities[1]) / (densities[2] - densities[1])), | |
voxelOffset + mix(cubeCorners[2], cubeCorners[3], (-densities[2]) / (densities[3] - densities[2])), | |
voxelOffset + mix(cubeCorners[3], cubeCorners[0], (-densities[3]) / (densities[0] - densities[3])), | |
voxelOffset + mix(cubeCorners[4], cubeCorners[5], (-densities[4]) / (densities[5] - densities[4])), | |
voxelOffset + mix(cubeCorners[5], cubeCorners[6], (-densities[5]) / (densities[6] - densities[5])), | |
voxelOffset + mix(cubeCorners[6], cubeCorners[7], (-densities[6]) / (densities[7] - densities[6])), | |
voxelOffset + mix(cubeCorners[7], cubeCorners[4], (-densities[7]) / (densities[4] - densities[7])), | |
voxelOffset + mix(cubeCorners[0], cubeCorners[4], (-densities[0]) / (densities[4] - densities[0])), | |
voxelOffset + mix(cubeCorners[1], cubeCorners[5], (-densities[1]) / (densities[5] - densities[1])), | |
voxelOffset + mix(cubeCorners[2], cubeCorners[6], (-densities[2]) / (densities[6] - densities[2])), | |
voxelOffset + mix(cubeCorners[3], cubeCorners[7], (-densities[3]) / (densities[7] - densities[3])), | |
}; | |
// Connect edges, generate triangles | |
for (uint i = 0; i < 15; i += 3) | |
{ | |
const uint j = marchingCase * 15 + i; | |
const ivec3 edges = ivec3(edgesTable[j], edgesTable[j + 1], edgesTable[j + 2]); | |
if (edges[0] < 0) return; | |
vec3 verts[3]; | |
const uint vertexIndex = atomicAdd(chunk.index, 3); | |
for (uint j = 0; j < 3; ++j) | |
{ | |
const float volumeRes = float(blockResolution + 4); | |
const float d = 1.f / volumeRes; | |
const vec3 vert = vertPerEdge[edges[j]]; | |
const vec3 pos = vert / (blockResolution / blockSize); | |
const vec3 uvw = (vert + 2.f) / volumeRes; | |
// Uncomment block for smooth normals | |
/* const vec3 norm = normalize(vec3( | |
texture(densityMap, uvw + vec3( d, 0.f, 0.f)).r - texture(densityMap, uvw - vec3( d, 0.f, 0.f)).r, | |
texture(densityMap, uvw + vec3(0.f, d, 0.f)).r - texture(densityMap, uvw - vec3(0.f, d, 0.f)).r, | |
texture(densityMap, uvw + vec3(0.f, 0.f, d)).r - texture(densityMap, uvw - vec3(0.f, 0.f, d)).r | |
)); | |
chunk.vertices[vertexIndex + j] = Vertex(origin + pos, -norm); */ | |
verts[j] = pos; | |
} | |
// Comment the following 6 lines for smooth normals | |
const vec3 d0 = normalize(verts[1] - verts[0]); | |
const vec3 d1 = normalize(verts[2] - verts[0]); | |
const vec3 norm = normalize(cross(d0, d1)); | |
chunk.vertices[vertexIndex + 0] = Vertex(origin + verts[0], norm); | |
chunk.vertices[vertexIndex + 1] = Vertex(origin + verts[1], norm); | |
chunk.vertices[vertexIndex + 2] = Vertex(origin + verts[2], norm); | |
}; | |
} | |
layout(local_size_x = 32, local_size_y = 1, local_size_z = 32) in; | |
void main() | |
{ | |
const ivec3 voxelIndex = ivec3(gl_GlobalInvocationID); | |
if (voxelIndex.x < blockResolution && voxelIndex.z < blockResolution) | |
march(voxelIndex); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment