Skip to content

Instantly share code, notes, and snippets.

@sneppy
Created May 11, 2019 23:26
Show Gist options
  • Save sneppy/42aa5fc75e56c8738b1ae122c602aa90 to your computer and use it in GitHub Desktop.
Save sneppy/42aa5fc75e56c8738b1ae122c602aa90 to your computer and use it in GitHub Desktop.
Procedural terrain generation + volumetric fog rendering
//////////////////////////////////////////////////
// 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);
}
//////////////////////////////////////////////////
// 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;
}
//////////////////////////////////////////////////
// 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;
}
//////////////////////////////////////////////////
// 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));
}
/**
* 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 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 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