Initial commit: Final state of the master project

This commit is contained in:
2017-09-16 09:41:37 +02:00
commit 696180d43b
832 changed files with 169717 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
#version 330 core
#define $colorType$
in vec3 pos;
in vec3 normal;
#if defined(TEXTURE_COLORS) || defined(TEXTURE_AND_VERTEX_COLORS)
in vec2 uv;
#endif
#if defined(VERTEX_COLORS) || defined(TEXTURE_AND_VERTEX_COLORS)
in vec3 vertColor;
#endif
uniform sampler2D textureSampler;
uniform sampler2D lastDepthMap;
uniform float reflectivity;
uniform bool firstPass;
uniform float depthMargin;
uniform vec3 cameraDir;
layout (location = 0) out vec3 color;
//layout (location = 1) out float depthOut;
layout (location = 1) out float angleOut;
layout (location = 2) out vec3 normalOut;
layout (location = 3) out float reflectivityOut;
void main() {
#if defined(TEXTURE_COLORS)
vec4 fullColor = texture(textureSampler, uv);
#endif
#if defined(VERTEX_COLORS)
vec4 fullColor = vec4(vertColor, 1.0f);
#endif
#if defined(TEXTURE_AND_VERTEX_COLORS)
vec4 fullColor = texture(textureSampler, uv) * vec4(vertColor, 1.0f);
#endif
#ifdef NO_COLORS
vec4 fullColor = vec4(1.0f);
#endif
//Discard transparent fragments
if (fullColor.a < 0.5) {
discard;
}
else
{
float lastDepth = texelFetch(lastDepthMap, ivec2(gl_FragCoord.x, gl_FragCoord.y), 0).r;
if (!firstPass && (lastDepth - gl_FragCoord.z) >= -depthMargin)
{
discard;
}
color = fullColor.rgb;
//vec3 lightDirection = normalize(vec3(0.25, -0.6, -1));
//color *= 0.3 + 0.7 * max(dot(-lightDirection, normalize(normal)), 0);
// Scale the normal between 0 and 1
normalOut = 0.5 + (normal * 0.5);
// Calculate the angle of the normal of this position with the camera
//angleOut = 1.;
if (normal == vec3(0))
angleOut = 1;
else
angleOut = abs(dot(cameraDir, normalize(normal)));
}
reflectivityOut = reflectivity;
}

View File

@@ -0,0 +1,32 @@
#version 330 core
#define $colorType$
layout (location = $vertexPosition$) in vec3 vertexPosition;
layout (location = $vertexNormal$) in vec3 vertexNormal;
uniform mat4 MVP;
out vec3 pos;
out vec3 normal;
#if defined(TEXTURE_COLORS) || defined(TEXTURE_AND_VERTEX_COLORS)
layout(location = $vertexUV$) in vec2 vertexUV;
out vec2 uv;
#endif
#if defined(VERTEX_COLORS) || defined(TEXTURE_AND_VERTEX_COLORS)
layout(location = $vertexColor$) in vec3 vertexColor;
out vec3 vertColor;
#endif
void main() {
pos = vertexPosition;
normal = vertexNormal;
gl_Position = MVP * vec4(vertexPosition, 1.0f);
#if defined(TEXTURE_COLORS) || defined(TEXTURE_AND_VERTEX_COLORS)
uv = vertexUV;
#endif
#if defined(VERTEX_COLORS) || defined(TEXTURE_AND_VERTEX_COLORS)
vertColor = vertexColor;
#endif
}

45
Research/shaders/GI.frag Normal file
View File

@@ -0,0 +1,45 @@
#version 430 core
in vec2 UV;
out vec4 color;
uniform sampler2D shadeless;
uniform sampler2D direct;
uniform sampler2D indirect;
const int kernelSize = 5;
const float[kernelSize][kernelSize] kernel = {
{ 2.0, 4.0, 5.0, 4.0, 2.0 },
{ 4.0, 9.0, 12.0, 9.0, 4.0 },
{ 5.0, 12.0, 15.0, 12.0, 5.0 },
{ 4.0, 9.0, 12.0, 9.0, 4.0 },
{ 2.0, 4.0, 5.0, 4.0, 2.0 },
};
const float kernelNormalizer = 1.0 / 115.0;
const float lightStrength = 1.0;
void main(void)
{
ivec2 screenSize = textureSize(shadeless, 0);
vec4 smoothIndirect = vec4(0);
ivec2 startCoord = ivec2(screenSize * UV) - ((kernelSize - 1) / 2);
for (int i = 0; i < kernelSize; i++) {
for (int j = 0; j < kernelSize; j++) {
ivec2 coord = startCoord + ivec2(i, j);
if (coord.x >= 0 && coord.y >= 0)
smoothIndirect += kernel[i][j] * texelFetch(indirect, coord, 0);
}
}
smoothIndirect *= kernelNormalizer * lightStrength;
vec4 shadelessPx = texture(shadeless, UV);
vec4 directPx = texture(direct, UV);
//color = shadelessPx * smoothIndirect;
color = shadelessPx * directPx + shadelessPx * smoothIndirect;
//color = texture(indirect, UV);
}

View File

@@ -0,0 +1,254 @@
#version 440 core
in vec3 pos;
in vec2 uv;
in vec3 normal;
out vec4 color;
uniform sampler2D textureSampler;
uniform usampler3D octreeSampler;
layout(location = $width$) uniform int width;
layout(location = $height$) uniform int height;
layout(location = $angle$) uniform float angle;
layout(location = $aspect$) uniform float aspect;
layout(location = $lightDirection$) uniform vec3 lightDirection;
layout(location = $cameraPosition$) uniform vec3 cameraPosition;
ivec3 texSize;
//************************************
// Intersection test between ray and cube, also gives intersection points as p0 + tmin * ray and p0 + tmax * ray
//************************************
bool rayCube(vec3 p0, vec3 ray, vec3 center, float extent, out float tmin, out float tmax, out uint minFace, out uint maxFace) {
// Translate ray origin based on cube center
p0 -= center;
// Get t from ray and cube's plane equations and use it to get the intersection coordinates
float t1 = -(dot(p0, vec3(1., 0., 0.)) - extent) / dot(ray, vec3(1., 0., 0.)); vec3 test1 = p0 + t1*ray;
float t2 = -(dot(p0, vec3(-1., 0., 0.)) - extent) / dot(ray, vec3(-1., 0., 0.)); vec3 test2 = p0 + t2*ray;
float t3 = -(dot(p0, vec3(0., 1., 0.)) - extent) / dot(ray, vec3(0., 1., 0.)); vec3 test3 = p0 + t3*ray;
float t4 = -(dot(p0, vec3(0., -1., 0.)) - extent) / dot(ray, vec3(0., -1., 0.)); vec3 test4 = p0 + t4*ray;
float t5 = -(dot(p0, vec3(0., 0., 1.)) - extent) / dot(ray, vec3(0., 0., 1.)); vec3 test5 = p0 + t5*ray;
float t6 = -(dot(p0, vec3(0., 0., -1.)) - extent) / dot(ray, vec3(0., 0., -1.)); vec3 test6 = p0 + t6*ray;
// Check if t was not negative and that the ray-plane intersection falls within the cube face
if (t1 < 0. || any(greaterThan(test1.yz, vec2(extent))) || any(lessThan(test1.yz, vec2(-extent))))
t1 = 0.;
if (t2 < 0. || any(greaterThan(test2.yz, vec2(extent))) || any(lessThan(test2.yz, vec2(-extent))))
t2 = 0.;
if (t3 < 0. || any(greaterThan(test3.xz, vec2(extent))) || any(lessThan(test3.xz, vec2(-extent))))
t3 = 0.;
if (t4 < 0. || any(greaterThan(test4.xz, vec2(extent))) || any(lessThan(test4.xz, vec2(-extent))))
t4 = 0.;
if (t5 < 0. || any(greaterThan(test5.xy, vec2(extent))) || any(lessThan(test5.xy, vec2(-extent))))
t5 = 0.;
if (t6 < 0. || any(greaterThan(test6.xy, vec2(extent))) || any(lessThan(test6.xy, vec2(-extent))))
t6 = 0.;
// Initialize tmin and tmax values that define the two intersection points
tmin = 9999999999.;
tmax = 0.;
// Use the lowest value of t that is not 0 for tmin
if (t1 > 0. && t1 < tmin) { tmin = t1; minFace = 1; }
if (t2 > 0. && t2 < tmin) { tmin = t2; minFace = 2; }
if (t3 > 0. && t3 < tmin) { tmin = t3; minFace = 3; }
if (t4 > 0. && t4 < tmin) { tmin = t4; minFace = 4; }
if (t5 > 0. && t5 < tmin) { tmin = t5; minFace = 5; }
if (t6 > 0. && t6 < tmin) { tmin = t6; minFace = 6; }
// Use the highest value of t that is not 0 for tmax
if (t1 > 0. && t1 > tmax) { tmax = t1; maxFace = 1; }
if (t2 > 0. && t2 > tmax) { tmax = t2; maxFace = 2; }
if (t3 > 0. && t3 > tmax) { tmax = t3; maxFace = 3; }
if (t4 > 0. && t4 > tmax) { tmax = t4; maxFace = 4; }
if (t5 > 0. && t5 > tmax) { tmax = t5; maxFace = 5; }
if (t6 > 0. && t6 > tmax) { tmax = t6; maxFace = 6; }
// If tmin = tmax, the ray origin is within the cube, so set tmin to 0
if (tmin == tmax)
tmin = 0.;
// If tmax is not 0, an intersection was found
return tmax > 0.;
}
uvec3 getUloc(vec3 p0, vec3 ray, float t, uint hitFace)
{
vec3 samplePoint = p0 + t * ray;
vec3 raySign = clamp(sign(ray), -1, 0);
// Calculate the location of the first leaf node that the ray enters
uvec3 uLoc = uvec3(floor(samplePoint));
// If the ray is moving in a negative direction, then the uLoc will be one position above the desired position.
switch(hitFace)
{
case 1: case 2: uLoc.x = uint(int(round(samplePoint.x + raySign.x))); break;
case 3: case 4: uLoc.y = uint(int(round(samplePoint.y + raySign.y))); break;
case 5: case 6: uLoc.z = uint(int(round(samplePoint.z + raySign.z))); break;
}
return uLoc;
}
ivec3 wrapNodePointer(in ivec3 samplePos)
{
// Make sure that the indices are wrapped if the pointer doesn't fit in the current texture
if (samplePos.x >= texSize.x)
{
samplePos.x = samplePos.x % texSize.x;
samplePos.y++;
if (samplePos.y >= texSize.y)
{
samplePos.y = samplePos.y % texSize.y;
samplePos.z++;
}
//return vec4(255, 255, 0, float($max_level$ + 1));
}
return samplePos;
}
//************************************
// Given a pointer to a node, and it's childmask, and some child index
// returns the nodePointer to the child at the given index.
// Note that if the child doesn't exist, it still returns some pointer as if it would.
//************************************
uvec3 getNodePointer(uvec3 curNodePointer, uint childMask, uint childIndex)
{
// Based on the childIndex and the childmask, find out what the index of the childpointer is
int childPointerIndex = bitCount(childMask << (31 - childIndex));
// Find sample position in 3D texture corresponding to current node and locat
ivec3 samplePos = ivec3(curNodePointer);
samplePos.x += childPointerIndex;
samplePos = wrapNodePointer(samplePos);
// Fetch the pointer from the texture
return texelFetch(octreeSampler, samplePos, 0).rgb;
}
// Fetches the childmask of the node at curNodePointer (note that this is not a mask for leaf nodes!)
uint getChildMask(in uvec3 nodePointer)
{
return texelFetch(octreeSampler, ivec3(nodePointer), 0).b;
}
bool hasChild(in uint childMask, in uint childIndex)
{
uint mask = 1 << childIndex;
return (childMask & mask) == mask;
}
uint getChildIndex(uvec3 uLoc, uint level)
{
uint range = 1 << ($max_level$ - level);
return ((uLoc.x & range) == range ? 1 : 0)
+ ((uLoc.y & range) == range ? 2 : 0)
+ ((uLoc.z & range) == range ? 4 : 0);
}
uvec4 sampleOctree(in uvec3 uLoc)
{
uvec3 result = uvec3(0);
uint childMask; uint childIndex;
// Traverse octree
uint i = 0;
for (; i <= $max_level$; ++i) {
// Read the childmask (stored in "blue"):
childMask = getChildMask(result);
// Find the index of the child in which the sampleposition is located
childIndex = getChildIndex(uLoc, i);
if (!hasChild(childMask, childIndex))
break;
result = getNodePointer(result, childMask, childIndex);
}
// Return level reached
return uvec4(result, i);
}
uvec3 getColor(uvec3 nodePointer)
{
return texelFetch(octreeSampler, ivec3(nodePointer), 0).rgb;
}
void main() {
uint maxLoop = 300;
texSize = textureSize(octreeSampler, 0);
// Set color using textures
color = texture(textureSampler, uv);
// Scale the grid so that all leaf cells have a width of exactly 1:
float extent = $extent$;
uint range = 1 << ($max_level$ + 1);
float rangeF = float(range);
float scale = rangeF / (extent * 2.);
float offset = extent + 0.5 / scale;
// Get ray and sample point
vec3 ray = normalize(pos - cameraPosition);
uvec3 raySignMask = uvec3(clamp(sign(-ray), 0, 1));
vec3 p0 = (cameraPosition + offset) * scale;
// Calculate the new position of the root
vec3 rootCenter = vec3(range) * 0.5;
float rootExtent = float(range) * 0.5;
// Initialize some variables
float tmin, tmax, tminRoot, tmaxRoot;
uint minFace, maxFace;
float leafExtent = 0.5;
// Start the ray at the edge of the root cube:
bool collision = rayCube(p0, ray, rootCenter, rootExtent, tminRoot, tmaxRoot, minFace, maxFace);
if (!collision) // Ray doesn't intersect with root.
{
//color.rgb = vec3(0,0,1);
return;
}
// Find cell through which the ray enters
uvec3 uLoc = getUloc(p0, ray, tminRoot, minFace);
// Keep testing ray box intersection, but prevent endless looping
int loop = 0;
while (!any(greaterThanEqual(uLoc, uvec3(range))) && !any(lessThan(uLoc, uvec3(0))) && loop < maxLoop) {
// Check if the current voxel contains geometry, and if so draw it
uvec4 result = sampleOctree(uLoc);
if (result.w >= $max_level$ + 1)
{
//color.rgb = vec3(result) / 255.;
color.rgb = vec3(float(loop) / 20.);
break;
}
//uint reachedLevelRange = 1 << ($max_level$ - result.w + 1);
//float reachedLevelExtent = float(reachedLevelRange) * 0.5;
//reachedLevelExtent = 0.5;
// Otherwise, advance to the next voxel
vec3 curCubeCenter = uLoc + leafExtent;
if (!rayCube(p0, ray, curCubeCenter, leafExtent, tmin, tmax, minFace, maxFace)) {
//color.rgb = vec3(1, float(maxFace) / 6., 0);
color.rgb = uLoc / range;
break;
}
uLoc = getUloc(p0, ray, tmax, maxFace);
++loop;
}
if (loop == maxLoop)
color.rgb = vec3(1, 0, 0);
// Discard pixels that are transparent
if (color.a < 0.5)
discard;
}

View File

@@ -0,0 +1,19 @@
#version 330 core
in vec2 UV;
out vec3 color;
uniform sampler2D depthTexture;
uniform sampler2D renderTexture;
uniform bool showDepth = false;
void main(){
if (showDepth)
{
color = vec3(texture2D( depthTexture, UV ).r ) ;
} else
{
color = texture(renderTexture, UV).xyz;
}
}

View File

@@ -0,0 +1,12 @@
#version 330 core
// Input vertex data, different for all executions of this shader.
layout(location = 0) in vec3 vertexPosition_modelspace;
// Output data ; will be interpolated for each fragment.
out vec2 UV;
void main(){
gl_Position = vec4(vertexPosition_modelspace,1);
UV = (vertexPosition_modelspace.xy+vec2(1,1))/2.0;
}

View File

@@ -0,0 +1,87 @@
#version 430 core
in vec2 UV;
out vec4 color;
uniform sampler2D depthTexture;
uniform sampler2D renderTexture;
//layout(location = $MVP$) uniform mat4 MVP;
//layout(location = $invMVP$) uniform mat4 invMVP;
const float total_strength = 1.0;
const float base = 0.2;
const float area = 0.0075;
const float falloff = 0.00005;
const float radius = 0.0002;
const int sampleCount = 20;
const vec3 samples[20] = {
vec3(0.576112, -0.307923, 0.273497), vec3(-0.239062, 0.014342, -0.512609),
vec3(-0.578527, 0.692620, 0.009409), vec3(-0.496125, 0.306383, -0.508048),
vec3(-0.620389, 0.266880, 0.483354), vec3(-0.478377, 0.521577, 0.611179),
vec3(0.250791, 0.168587, -0.066709), vec3(0.936988, -0.118714, -0.235831),
vec3(0.310220, 0.289636, 0.046191), vec3(-0.604985, 0.514587, 0.180593),
vec3(0.253899, 0.730909, 0.587989), vec3(-0.097092, -0.047744, -0.110424),
vec3(-0.357070, 0.877361, 0.020189), vec3(0.550173, 0.220257, -0.754331),
vec3(0.574418, 0.637574, -0.445669), vec3(0.170666, -0.597387, -0.366459),
vec3(0.269777, 0.234300, -0.577843), vec3(-0.702647, -0.090661, -0.628000),
vec3(-0.798998, -0.013601, 0.191734),vec3(-0.229475, 0.020532, -0.938973)
};
vec3 normalFromDepth(float depth, vec2 UV) {
const vec2 offset1 = vec2(0.0, 0.001);
const vec2 offset2 = vec2(0.001, 0.0);
float depth1 = texture(depthTexture, UV + offset1).r;
float depth2 = texture(depthTexture, UV + offset2).r;
vec3 p1 = vec3(offset1, depth1 - depth);
vec3 p2 = vec3(offset2, depth2 - depth);
vec3 normal = cross(p1, p2);
normal.z = -normal.z;
return normalize(normal);
}
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
float ssao()
{
// Stolen from: http://theorangeduck.com/page/pure-depth-ssao
float depth = texture(depthTexture, UV).r;
vec3 random = vec3(rand(UV), rand(vec2(UV.x, UV.y + 1.0)), rand(vec2(UV.x + 1.0, UV.y)));
vec3 position = vec3(UV, depth);
vec3 normal = normalFromDepth(depth, UV);
float radius_depth = radius/depth;
float occlusion = 0.0;
for(int i=0; i < sampleCount; i++) {
vec3 ray = radius_depth * reflect(samples[i], random);
vec3 hemi_ray = position + sign(dot(ray,normal)) * ray;
float occ_depth = texture(depthTexture, clamp(hemi_ray.xy, vec2(0.0), vec2(1.0))).r;
float difference = depth - occ_depth;
occlusion += step(falloff, difference) * (1.0-smoothstep(falloff, area, difference));
}
return 1.0 - total_strength * occlusion * (1.0 / float(sampleCount));
}
void main()
{
//color = texture(renderTexture, UV).rgba;
float depth = texture(depthTexture, UV).r;
color = texture(renderTexture, UV).rgba;
if (depth < 0.98)
{
color *= ssao();
}
//color = vec4(vec3(depth), 1);
}

165
Research/shaders/SSAO2.frag Normal file
View File

@@ -0,0 +1,165 @@
#version 430 core
/*
SSAO GLSL shader v1.2
assembled by Martins Upitis (martinsh) (devlog-martinsh.blogspot.com)
original technique is made by Arkano22 (www.gamedev.net/topic/550699-ssao-no-halo-artifacts/)
changelog:
1.2 - added fog calculation to mask AO. Minor fixes.
1.1 - added spiral sampling method from here:
(http://www.cgafaq.info/wiki/Evenly_distributed_points_on_sphere)
*/
in vec2 UV;
out vec4 color;
uniform sampler2D depthTexture;
uniform sampler2D renderTexture;
layout(location = $width$) uniform int renderTextureWidth;
layout(location = $height$) uniform int renderTextureHeight;
#define PI 3.14159265
float width = renderTextureWidth; //texture width
float height = renderTextureHeight; //texture height
//------------------------------------------
//general stuff
//make sure that these two values are the same for your camera, otherwise distances will be wrong.
float znear = 100.0; //Z-near
float zfar = 1024.0; //Z-far
//user variables
int samples = 32; //ao sample count
float radius = 2.0; //ao radius
float aoclamp = 0.001; //depth clamp - reduces haloing at screen edges
bool noise = true; //use noise instead of pattern for sample dithering
float noiseamount = 0.0002; //dithering amount
float diffarea = 0.05; //self-shadowing reduction
float gdisplace = 0.05; //gauss bell center
bool onlyAO = false; //use only ambient occlusion pass?
float lumInfluence = 0.2; //how much luminance affects occlusion
//--------------------------------------------------------
vec2 rand(vec2 coord) //generating noise/pattern texture for dithering
{
float noiseX = ((fract(1.0-coord.s*(width/2.0))*0.25)+(fract(coord.t*(height/2.0))*0.75))*2.0-1.0;
float noiseY = ((fract(1.0-coord.s*(width/2.0))*0.75)+(fract(coord.t*(height/2.0))*0.25))*2.0-1.0;
if (noise)
{
noiseX = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233))) * 43758.5453),0.0,1.0)*2.0-1.0;
noiseY = clamp(fract(sin(dot(coord ,vec2(12.9898,78.233)*2.0)) * 43758.5453),0.0,1.0)*2.0-1.0;
}
return vec2(noiseX,noiseY)*noiseamount;
}
float readDepth(in vec2 coord)
{
if (UV.x < 0.0 || UV.y < 0.0) return 1.0;
return (2.0 * znear) / (zfar + znear - texture2D(depthTexture, coord).x * (zfar-znear));
}
float compareDepths(in float depth1, in float depth2,inout int far)
{
float garea = 2.0; //gauss bell width
float diff = (depth1 - depth2)*100.0; //depth difference (0-100)
//reduce left bell width to avoid self-shadowing
if (diff<gdisplace)
{
garea = diffarea;
}else{
far = 1;
}
float gauss = pow(2.7182,-2.0*(diff-gdisplace)*(diff-gdisplace)/(garea*garea));
return gauss;
}
float calAO(float depth,float dw, float dh)
{
float dd = (1.0-depth)*radius;
float temp = 0.0;
float temp2 = 0.0;
float coordw = UV.x + dw*dd;
float coordh = UV.y + dh*dd;
float coordw2 = UV.x - dw*dd;
float coordh2 = UV.y - dh*dd;
vec2 coord = vec2(coordw , coordh);
vec2 coord2 = vec2(coordw2, coordh2);
int far = 0;
temp = compareDepths(depth, readDepth(coord),far);
//DEPTH EXTRAPOLATION:
if (far > 0)
{
temp2 = compareDepths(readDepth(coord2),depth,far);
temp += (1.0-temp)*temp2;
}
return temp;
}
void main(void)
{
vec2 noise = rand(UV);
float depth = readDepth(UV);
//color = vec4(vec3(texture(depthTexture, UV).x), 1);
//color = vec4(vec3(depth) * 10.0, 1);
//return;
float w = (1.0 / width)/clamp(depth,aoclamp,1.0)+(noise.x*(1.0-noise.x));
float h = (1.0 / height)/clamp(depth,aoclamp,1.0)+(noise.y*(1.0-noise.y));
float pw;
float ph;
float ao = 0;
float dl = PI*(3.0-sqrt(5.0));
float dz = 1.0/float(samples);
float l = 0.0;
float z = 1.0 - dz/2.0;
for (int i = 0; i <= samples; i ++)
{
float r = sqrt(1.0-z);
pw = cos(l)*r;
ph = sin(l)*r;
ao += calAO(depth,pw*w,ph*h);
z = z - dz;
l = l + dl;
}
ao /= float(samples);
ao = 1.0-ao;
//ao = pow(ao, 2.0);
//color = vec4(vec3(ao), 1);
//return;
vec3 renderColor = texture(renderTexture, UV).rgb;
vec3 lumcoeff = vec3(0.299,0.587,0.114);
float lum = dot(renderColor, lumcoeff);
vec3 luminance = vec3(lum, lum, lum);
//color = vec4(vec3(lum), 1);
//return;
vec3 final = vec3(renderColor*mix(vec3(ao),vec3(1.0),luminance*lumInfluence));//mix(color*ao, white, luminance)
if (onlyAO)
final = vec3(mix(vec3(ao),vec3(1.0),luminance*lumInfluence)); //ambient occlusion only
color = vec4(final, 1.0);
}

189
Research/shaders/SSDS.frag Normal file
View File

@@ -0,0 +1,189 @@
#version 430 core
#define $Material$
#if defined(MT_NORMAL) | defined(MT_COLORNORMAL) | defined(MT_COLORNORMALREFLECTIVITY)
const bool SHADE_DIFFUSE = false;
#else
const bool SHADE_DIFFUSE = true;
#endif
in vec2 UV;
out vec4 color;
uniform sampler2D depthTexture;
uniform sampler2D renderTexture;
uniform sampler2D voxelPositionsTexture;
layout(location = $width$) uniform int width;
layout(location = $height$) uniform int height;
layout(location = $MVP$) uniform mat4x4 MVP;
layout(location = $lightDirection$) uniform vec3 lightDirection;
layout(location = $lightType$) uniform int lightType;
const uint range = 1 << ($max_level$ + 1);
const float extent = $extent$;
const float rangeF = float(range);
const float scale = rangeF / (extent * 2.);
const float offset = extent + 0.5 / scale;
const int SAMPLE_DISTANCE = 3;
const float MAX_DISTANCE = 0.02 * rangeF;
const float ssaoRadius = 2.0;
const float ssaoPower = 2.0;
#define SSAO_KERNEL_SIZE 10
const vec3[SSAO_KERNEL_SIZE] SSAO_KERNEL = {
vec3(-0.013627260795642221, -0.08986502062675834, 0.04169623281496606),
vec3(0.1530762079182679, -0.07153954612140453, 0.08688940044738859),
vec3(0.07090584436998379, 0.1490828023178984, 0.22615631604538947),
vec3(-0.15616336851550036, -0.26564930875054044, 0.20480099388003975),
vec3(0.26677241220752623, 0.28676826613128037, 0.2412393865541797),
vec3(-0.3475187558492847, -0.15578006191669713, 0.39681643948076967),
vec3(0.3887426730563284, 0.02398677725666013, 0.5078422675022826),
vec3(0.49626198926704007, -0.21014511667561903, 0.49240538984266874),
vec3(-0.5032145340980271, 0.5942213554886051, 0.2570527443031691),
vec3(0.5983551080400994, 0.25120169632241235, 0.6379411198904361)
};
#define PI 3.14159265
//vec3 screenToWorld(vec3 screen) {
// vec4 homogeniousWorld = invMVP * vec4(screen * 2.0 - 1.0, 1);
// homogeniousWorld /= homogeniousWorld.z;
// return homogeniousWorld.xyz;
//}
vec3 NormalFromTriangleVertices(vec3 triangleVertices[3])
{
// now is same as RedBook (OpenGL Programming Guide)
vec3 u = triangleVertices[0] - triangleVertices[1];
vec3 v = triangleVertices[1] - triangleVertices[2];
return normalize(cross(u, v));
}
float getDiffuseLight(vec3 normal, vec3 position)
{
if (lightType == 0) return 1.0;
else if (lightType == 1) return max(dot(-lightDirection, normal), 0);
else if (lightType == 2)
{
vec3 toLight = lightDirection / scale - position;
vec3 lightDir = normalize(toLight);
return max(dot(lightDir, normal), 0);
}
}
float rand(vec2 n)
{
return 0.5 + 0.5 * fract(sin(dot(n.xy, vec2(12.9898, 78.233)))* 43758.5453);
}
vec3 getTangent(vec3 normal)
{
vec3 tangent;
vec3 c1 = cross(normal, vec3(0.0, 0.0, 1.0));
vec3 c2 = cross(normal, vec3(0.0, 1.0, 0.0));
if (length(c1)>length(c2))
tangent = c1;
else
tangent = c2;
return normalize(tangent);
}
vec3 voxelGridToWorld(vec3 voxelGridCoord)
{
return (voxelGridCoord / scale) - offset;
}
vec4 worldToScreen(vec3 sam)
{
vec4 samUV = vec4(sam , 1.0);
samUV = MVP * samUV;
samUV.xy /= samUV.w;
samUV.xy = samUV.xy * 0.5 + 0.5;
return samUV;
}
// Calculates the SSAO occlusion
float SSAO(vec3 pos, vec3 normal)
{
// Rotate our kernel
vec3 rvec = vec3(rand(UV), rand(normal.xy), rand(pos.xy));
vec3 tangent = normalize(rvec - normal * dot(rvec, normal));
vec3 bitangent = cross(normal, tangent);
mat3 tbn = mat3(tangent, bitangent, normal);
float occlusion = 0.0;
for (int i = 0; i < SSAO_KERNEL_SIZE; i++) {
// get sample position:
vec3 sam = tbn * SSAO_KERNEL[i];
sam = sam * ssaoRadius + pos;
vec3 towardsSam = sam - pos;
float samDot = dot(normal, towardsSam);
// project sample position:
vec4 samUV = worldToScreen(sam);
// get sample depth:
vec3 sampledPos = voxelGridToWorld(texture(voxelPositionsTexture, samUV.xy).xyz);
vec3 towardsSampledPos = sampledPos - pos;
float sampledDot = dot(normal, towardsSampledPos);
//color = vec4(towardsSampledPos, 1);
//return abs(sampledDot * 1.0) / rangeF;
// range check & accumulate:
float rangeCheck = abs(sampledDot) < ssaoRadius ? 1.0 : 0.0;
occlusion += (sampledDot >= samDot ? 1.0 : 0.0) * rangeCheck;
}
return pow(1.0 - (occlusion / float(SSAO_KERNEL_SIZE)), ssaoPower);
}
void main(void)
{
color = texture(renderTexture, UV);
// Get the voxel positions in the center, left, right, top bottom:
ivec2 screenCoord = ivec2(UV * vec2(width, height));
vec3 c = texelFetch(voxelPositionsTexture, screenCoord, 0).xyz;
vec3 pos = voxelGridToWorld(c);
if (c == vec3(0)) return; // If no vertex position is known, don't do further processing
vec3 l = texelFetch(voxelPositionsTexture, screenCoord + ivec2(-SAMPLE_DISTANCE, 0), 0).xyz;
vec3 r = texelFetch(voxelPositionsTexture, screenCoord + ivec2(+SAMPLE_DISTANCE, 0), 0).xyz;
vec3 t = texelFetch(voxelPositionsTexture, screenCoord + ivec2(0, +SAMPLE_DISTANCE), 0).xyz;
vec3 b = texelFetch(voxelPositionsTexture, screenCoord + ivec2(0, -SAMPLE_DISTANCE), 0).xyz;
// Check if we need to dispose any location as they are too far away (in world space);
bool ignoreL = length(l - c) > MAX_DISTANCE;
bool ignoreR = length(r - c) > MAX_DISTANCE;
bool ignoreT = length(t - c) > MAX_DISTANCE;
bool ignoreB = length(t - b) > MAX_DISTANCE;
vec3 normal = vec3(0);
// Make some triangles and calculate the normal based on those triangles
if (!ignoreT && !ignoreR) { vec3 triangle1[3] = { t, c, r }; normal += NormalFromTriangleVertices(triangle1); }
if (!ignoreR && !ignoreB) { vec3 triangle2[3] = { r, c, b }; normal += NormalFromTriangleVertices(triangle2); }
if (!ignoreB && !ignoreL) { vec3 triangle3[3] = { b, c, l }; normal += NormalFromTriangleVertices(triangle3); }
if (!ignoreL && !ignoreT) { vec3 triangle4[3] = { l, c, t }; normal += NormalFromTriangleVertices(triangle4); }
normal = normalize(normal);
//color = vec4(normal == vec3(0) ? 1.0 : 0.0); return;
//color = vec4(normal, 1); return;
if (SHADE_DIFFUSE)
color *= (0.4 + 0.6 * getDiffuseLight(normal, pos));
float occlusion = SSAO(pos, normal);
color *= occlusion;
}

View File

@@ -0,0 +1,234 @@
#version 440 core
in vec3 pos;
in vec2 uv;
in vec3 normal;
out vec4 color;
uniform sampler2D textureSampler;
uniform usampler3D octreeSampler;
layout(location = $width$) uniform int width;
layout(location = $height$) uniform int height;
layout(location = $angle$) uniform float angle;
layout(location = $aspect$) uniform float aspect;
layout(location = $lightDirection$) uniform vec3 lightDirection;
layout(location = $cameraPosition$) uniform vec3 cameraPosition;
//************************************
// Intersection test between ray and cube, also gives intersection points as p0 + tmin * ray and p0 + tmax * ray
//************************************
bool rayCube(vec3 p0, vec3 ray, vec3 center, float extent, out float tmin, out float tmax, out uint minFace, out uint maxFace) {
// Translate ray origin based on cube center
p0 -= center;
// Get t from ray and cube's plane equations and use it to get the intersection coordinates
float t1 = -(dot(p0, vec3(1., 0., 0.)) - extent) / dot(ray, vec3(1., 0., 0.)); vec3 test1 = p0 + t1*ray;
float t2 = -(dot(p0, vec3(-1., 0., 0.)) - extent) / dot(ray, vec3(-1., 0., 0.)); vec3 test2 = p0 + t2*ray;
float t3 = -(dot(p0, vec3(0., 1., 0.)) - extent) / dot(ray, vec3(0., 1., 0.)); vec3 test3 = p0 + t3*ray;
float t4 = -(dot(p0, vec3(0., -1., 0.)) - extent) / dot(ray, vec3(0., -1., 0.)); vec3 test4 = p0 + t4*ray;
float t5 = -(dot(p0, vec3(0., 0., 1.)) - extent) / dot(ray, vec3(0., 0., 1.)); vec3 test5 = p0 + t5*ray;
float t6 = -(dot(p0, vec3(0., 0., -1.)) - extent) / dot(ray, vec3(0., 0., -1.)); vec3 test6 = p0 + t6*ray;
// Check if t was not negative and that the ray-plane intersection falls within the cube face
if (t1 < 0. || any(greaterThanEqual(test1.yz, vec2(extent))) || any(lessThanEqual(test1.yz, vec2(-extent))))
t1 = 0.;
if (t2 < 0. || any(greaterThanEqual(test2.yz, vec2(extent))) || any(lessThanEqual(test2.yz, vec2(-extent))))
t2 = 0.;
if (t3 < 0. || any(greaterThanEqual(test3.xz, vec2(extent))) || any(lessThanEqual(test3.xz, vec2(-extent))))
t3 = 0.;
if (t4 < 0. || any(greaterThanEqual(test4.xz, vec2(extent))) || any(lessThanEqual(test4.xz, vec2(-extent))))
t4 = 0.;
if (t5 < 0. || any(greaterThanEqual(test5.xy, vec2(extent))) || any(lessThanEqual(test5.xy, vec2(-extent))))
t5 = 0.;
if (t6 < 0. || any(greaterThanEqual(test6.xy, vec2(extent))) || any(lessThanEqual(test6.xy, vec2(-extent))))
t6 = 0.;
// Initialize tmin and tmax values that define the two intersection points
tmin = 9999999999.;
tmax = 0.;
// Use the lowest value of t that is not 0 for tmin
if (t1 > 0. && t1 <= tmin) tmin = t1;
if (t2 > 0. && t2 <= tmin) tmin = t2;
if (t3 > 0. && t3 <= tmin) tmin = t3;
if (t4 > 0. && t4 <= tmin) tmin = t4;
if (t5 > 0. && t5 <= tmin) tmin = t5;
if (t6 > 0. && t6 <= tmin) tmin = t6;
// Use the highest value of t that is not 0 for tmax
if (t1 > 0. && t1 >= tmax) tmax = t1;
if (t2 > 0. && t2 >= tmax) tmax = t2;
if (t3 > 0. && t3 >= tmax) tmax = t3;
if (t4 > 0. && t4 >= tmax) tmax = t4;
if (t5 > 0. && t5 >= tmax) tmax = t5;
if (t6 > 0. && t6 >= tmax) tmax = t6;
// If tmin = tmax, the ray origin is within the cube, so set tmin to 0
if (tmin == tmax)
tmin = 0.;
// Figure out which face the ray exits
if (t1 == tmin) minFace = 1;
if (t2 == tmin) minFace = 2;
if (t3 == tmin) minFace = 3;
if (t4 == tmin) minFace = 4;
if (t5 == tmin) minFace = 5;
if (t6 == tmin) minFace = 6;
if (t1 == tmax) maxFace = 1;
if (t2 == tmax) maxFace = 2;
if (t3 == tmax) maxFace = 3;
if (t4 == tmax) maxFace = 4;
if (t5 == tmax) maxFace = 5;
if (t6 == tmax) maxFace = 6;
// If tmax is not 0, an intersection was found
return tmax > 0.;
}
//************************************
// Sample the octree at a location, return if geometry is present here and return deepest level reached when traversing
//************************************
uvec4 sampleOctree(vec3 loc) {
ivec3 texSize = textureSize(octreeSampler, 0);
// Set location in range (0,2^$max_level$)
uint range = 1 << $max_level$;
loc = range + range * loc / $extent$;
uvec3 uLoc = uvec3(loc.x, loc.y, loc.z);
uvec3 result = uvec3(0);
// Traverse octree
uint i = 0;
for (; i <= $max_level$; ++i) {
if (i == $max_level$)
{ // Leaf node was reached, fetch the full color:
return uvec4(texelFetch(octreeSampler, ivec3(result), 0).rgb, i);
}
// Read the childmask (stored in "blue"):
uint childMask = texelFetch(octreeSampler, ivec3(result), 0).b;
// If the mask is empty, we got a child node
if (childMask == 0)
break;
// Find the index of the child in which the sampleposition is located
range = 1 << ($max_level$ - i);
uint childIndex = ((uLoc.x & range) == range ? 1 : 0)
+ ((uLoc.y & range) == range ? 2 : 0)
+ ((uLoc.z & range) == range ? 4 : 0);
uint mask = 1 << childIndex;
if ((childMask & mask) == 0) // The child doesn't exist, even though this is not a leaf node
break;//return vec4(255, 255, 0, float($max_level$ + 1));
// Based on this index and the childmask, find out what the index of the childpointer is
uint childPointerIndex = 0;
for(int j = 0; j < 8; j++)
{
uint mask = 1 << j;
if ((childMask & mask) == mask)
childPointerIndex++;
if (j == childIndex)
break;
}
// Find sample position in 3D texture corresponding to current node and location
uvec3 samplePos = result;
samplePos.x += childPointerIndex;
if (samplePos.x >= texSize.x)
{
samplePos.x = samplePos.x % texSize.x;
samplePos.y++;
if (samplePos.y >= texSize.y)
{
samplePos.y = samplePos.y % texSize.y;
samplePos.z++;
}
//return vec4(255, 255, 0, float($max_level$ + 1));
}
result = texelFetch(octreeSampler, ivec3(samplePos), 0).rgb;
// If not, this is not a leaf node, so result actually contains a pointer to the 3D location of the child
// Multiply the location by two to go to next level
//loc *= 2.;
}
// Return level reached, and the pointer found there?
return uvec4(result, i);
}
void main() {
uint maxLoop = 4096;
// Set color using textures
color = texture(textureSampler, uv);
// Get ray and sample point
vec3 ray = normalize(pos - cameraPosition);
vec3 samplePoint = cameraPosition;
float extent = $extent$;
float tmin, tmax;
uint minFace = 0;
uint maxFace;
uint crap;
// Calculate the rayCube intersection with the root:
float tmaxRoot, tminRoot;
if (!rayCube(cameraPosition, ray, vec3(0.), extent, tminRoot, tmaxRoot, crap, crap))
return;
float t = tminRoot;
// Keep testing ray box intersection, but prevent endless looping
int loop = 0;
while (t < tmaxRoot && loop < maxLoop) {
samplePoint = cameraPosition + t * ray + 0.001 * ray;
// Sample octree at sample point and store reached level
uvec4 result = sampleOctree(samplePoint);
uint reached_level = result.w;
// If the reached level is max_level + 1, we have reached geometry, so color the pixel and break from loop
if (reached_level == $max_level$) {
color.rgb = /*(loop / 120.) * (0. + (float(minFace) * 0.16666666)) **/ (vec3(result) / 255.);
//color.rgb = samplePoint / $extent$;
break;
}
// Set cube center to 0 and current extent to overall extent
vec3 cubeCenter = vec3(0.);
float currentExtent = extent;
// Convert relative sample point to (-1,1) range for easier computations
vec3 relativeSamplePoint = samplePoint / extent;
// Loop through all reached levels
for (int j = 0; j <= reached_level; ++j) {
// At every level, compute the cube center of the node that was sampled at this level and the sample point relative to this node
cubeCenter += sign(relativeSamplePoint) * .5 * currentExtent;
relativeSamplePoint = sign(relativeSamplePoint) * (-1. + 2. * abs(relativeSamplePoint));
currentExtent *= .5;
}
// Use the obtained cube center to do another ray-box intersection test
rayCube(cameraPosition, ray, cubeCenter, currentExtent, tmin, tmax, minFace, maxFace);
t = tmax;
++loop;
}
if (loop == maxLoop)
color.rgb = vec3(1, 0,0);
// Discard pixels that are transparent
if (color.a < 0.5)
discard;
}

View File

@@ -0,0 +1,18 @@
#version 430 core
layout(location = $vertexPosition$) in vec3 vertexPosition;
layout(location = $vertexUV$) in vec2 vertexUV;
layout(location = $vertexNormal$) in vec3 vertexNormal;
layout(location = $MVP$) uniform mat4 MVP;
out vec3 pos;
out vec2 uv;
out vec3 normal;
void main() {
pos = vertexPosition;
uv = vertexUV;
normal = vertexNormal;
gl_Position = MVP * vec4(vertexPosition, 1.0f);
}

View File

@@ -0,0 +1,175 @@
#include "ShaderLoader.h"
#include <vector>
#include <fstream>
#include "../inc/gl/glew.h"
#include "../Renderer.h"
#include "../PropertyLoader.h"
ShaderLoader* ShaderLoader::mInstance = NULL;
void ShaderLoader::Create() {
if (mInstance == NULL)
mInstance = new ShaderLoader();
}
void ShaderLoader::Destroy() {
if (mInstance != NULL)
delete mInstance;
mInstance = NULL;
}
ShaderLoader* ShaderLoader::Instance() {
return mInstance;
}
void ShaderLoader::SetShaderPath(std::string shaderPath)
{
mShaderPath = shaderPath;
}
std::string ShaderLoader::GetShaderPath() {
return mShaderPath;
}
// Load shader
unsigned ShaderLoader::LoadShader(const char* vertexFileName, const char* fragmentFileName, std::map<std::string, std::string> additionalProperties) {
// Create the shaders
unsigned vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
unsigned fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
std::string vertexPath = mShaderPath;
std::string fragmentPath = mShaderPath;
vertexPath.append(vertexFileName);
fragmentPath.append(fragmentFileName);
// Read the vertex shader code from the file
std::string vertexShaderCode;
std::ifstream vertexShaderStream(vertexPath, std::ios::in);
if (vertexShaderStream.is_open()) {
std::string line;
while (getline(vertexShaderStream, line)) {
ParseProperties(line, '$', additionalProperties);
ParseProperties(line, '%', additionalProperties);
vertexShaderCode += "\n" + line;
}
vertexShaderStream.close();
}
// Read the fragment shader code from the file
std::string fragmentShaderCode;
std::ifstream fragmentShaderStream(fragmentPath, std::ios::in);
if (fragmentShaderStream.is_open()) {
std::string line = "";
while (getline(fragmentShaderStream, line)) {
ParseProperties(line, '$', additionalProperties);
ParseProperties(line, '%', additionalProperties);
fragmentShaderCode += "\n" + line;
}
fragmentShaderStream.close();
}
int result = GL_FALSE;
int infoLogLength;
// Compile vertex shader
if (verbose) printf("Compiling shader : %s\n", vertexFileName);
char const* vertexSourcePointer = vertexShaderCode.c_str();
glShaderSource(vertexShaderID, 1, &vertexSourcePointer, NULL);
glCompileShader(vertexShaderID);
// Check vertex shader
glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &result);
glGetShaderiv(vertexShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
if (infoLogLength > 0)
{
std::vector<char> vertexShaderErrorMessage(infoLogLength);
glGetShaderInfoLog(vertexShaderID, infoLogLength, NULL, &vertexShaderErrorMessage[0]);
fprintf(stdout, "%s\n", &vertexShaderErrorMessage[0]);
}
// Compile fragment shader
if (verbose) printf("Compiling shader : %s\n", fragmentFileName);
char const * fragmentSourcePointer = fragmentShaderCode.c_str();
glShaderSource(fragmentShaderID, 1, &fragmentSourcePointer, NULL);
glCompileShader(fragmentShaderID);
// Check fragment shader
glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &result);
glGetShaderiv(fragmentShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
if (infoLogLength > 0)
{
std::vector<char> fragmentShaderErrorMessage(infoLogLength);
glGetShaderInfoLog(fragmentShaderID, infoLogLength, NULL, &fragmentShaderErrorMessage[0]);
if (fragmentShaderErrorMessage.size() > 1)
fprintf(stdout, "%s\n", &fragmentShaderErrorMessage[0]);
}
// Link the program
if (verbose) fprintf(stdout, "Linking program\n");
unsigned programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glLinkProgram(programID);
// Check the program
glGetProgramiv(programID, GL_LINK_STATUS, &result);
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &infoLogLength);
std::vector<char> programErrorMessage(glm::max(infoLogLength, int(1)));
glGetProgramInfoLog(programID, infoLogLength, NULL, &programErrorMessage[0]);
if (programErrorMessage.size() > 1)
fprintf(stdout, "%s\n", &programErrorMessage[0]);
glDeleteShader(vertexShaderID);
glDeleteShader(fragmentShaderID);
return programID;
}
ShaderLoader::ShaderLoader() {
PropertyLoader::Create();
mShaderPath = "../Research/shaders/";
}
ShaderLoader::~ShaderLoader() {
}
//************************************
// Parse shader properties, i.e. text between delimiters
//************************************
void ShaderLoader::ParseProperties(std::string &line, char delimiter, std::map<std::string, std::string> additionalProperties) {
size_t open = 0, close = 0;
bool inProperty = false;
size_t i = 0;
while(i < line.size()) {
// Whenever a delimiter is found, toggle between in and out of property
if (line[i] == delimiter)
{
if (inProperty)
{
// If we were in a property, this delimiter marks the end of it.
// Replace all text between open and close by an additional property
close = i;
std::string identifier = line.substr(open + 1, close - open - 1);
auto addProp = additionalProperties.find(identifier);
std::string value;
if (addProp != additionalProperties.end())
value = addProp->second;
else
value = PropertyLoader::Instance()->GetProperty("shader_" + identifier);
if (value.empty()) return;
line.replace(open, close - open + 1, value);
i = open + value.size();
}
else
{
// If we were NOT in a property, this is the start of one
open = i;
}
inProperty = !inProperty;
}
i++;
}
}

View File

@@ -0,0 +1,27 @@
#pragma once
#include <string>
#include <map>
class ShaderLoader {
public:
static void Create();
static void Destroy();
static ShaderLoader* Instance();
unsigned LoadShader(const char* vertexFileName = "Shader.vert", const char* fragmentFileName = "Shader.frag", const std::map<std::string, std::string> additionalProperties = std::map<std::string, std::string>());
void SetShaderPath(std::string shaderPath);
std::string GetShaderPath();
protected:
private:
ShaderLoader();
~ShaderLoader();
void ParseProperties(std::string &line, char delimiter, const std::map<std::string, std::string> additionalProperties = std::map<std::string, std::string>());
static ShaderLoader* mInstance;
std::string mShaderPath;
bool verbose;
};

View File

@@ -0,0 +1,340 @@
#version 440 core
in vec3 pos;
in vec2 uv;
in vec3 normal;
out vec4 color;
uniform sampler2D textureSampler;
uniform usampler3D octreeSampler;
layout(location = $width$) uniform int width;
layout(location = $height$) uniform int height;
layout(location = $angle$) uniform float angle;
layout(location = $aspect$) uniform float aspect;
layout(location = $lightDirection$) uniform vec3 lightDirection;
layout(location = $cameraPosition$) uniform vec3 cameraPosition;
struct Ray
{
vec3 p0;
vec3 dir;
vec3 invDir;
vec3 dirSign;
uvec3 dirSignNegMask;
};
ivec3 texSize;
const uint range = 1 << ($max_level$ + 1);
const vec3 rootCenter = vec3(range) * 0.5;
const float rootExtent = float(range) * 0.5;
// Initialize the stack
uvec3 pointerStack[$max_level$ + 1];
vec3 positionStack[$max_level$ + 1];
float extentStack[$max_level$ + 1];
uint stackPos;
Ray getRay(vec3 p0, vec3 dir)
{
dir = normalize(dir);
vec3 dirSign = sign(dir);
return Ray(
p0,
dir,
1 / dir,
dirSign,
uvec3(-clamp(sign(dir), -1, 0)));
}
vec3 sampleRay(Ray ray, float time)
{
return ray.dir * time + ray.p0;
}
//************************************
// Intersection test between ray and cube, also gives intersection points as p0 + tmin * ray and p0 + tmax * ray
//************************************
bool rayCube(in Ray ray, in vec3 center, in float extent, out float tmin, out float tmax, out vec3 minAxis, out vec3 maxAxis) {
// Translate ray origin based on cube center
vec3 p0 = ray.p0 - center;
// Get t from ray and cube's plane equations and use it to get the intersection coordinates
float t1 = -(dot(p0, vec3(1., 0., 0.)) - extent) / dot(ray.dir, vec3(1., 0., 0.)); vec3 test1 = p0 + t1*ray.dir;
float t2 = -(dot(p0, vec3(-1., 0., 0.)) - extent) / dot(ray.dir, vec3(-1., 0., 0.)); vec3 test2 = p0 + t2*ray.dir;
float t3 = -(dot(p0, vec3(0., 1., 0.)) - extent) / dot(ray.dir, vec3(0., 1., 0.)); vec3 test3 = p0 + t3*ray.dir;
float t4 = -(dot(p0, vec3(0., -1., 0.)) - extent) / dot(ray.dir, vec3(0., -1., 0.)); vec3 test4 = p0 + t4*ray.dir;
float t5 = -(dot(p0, vec3(0., 0., 1.)) - extent) / dot(ray.dir, vec3(0., 0., 1.)); vec3 test5 = p0 + t5*ray.dir;
float t6 = -(dot(p0, vec3(0., 0., -1.)) - extent) / dot(ray.dir, vec3(0., 0., -1.)); vec3 test6 = p0 + t6*ray.dir;
// Check if t was not negative and that the ray-plane intersection falls within the cube face
if (t1 < 0. || any(greaterThan(test1.yz, vec2(extent))) || any(lessThan(test1.yz, vec2(-extent))))
t1 = 0.;
if (t2 < 0. || any(greaterThan(test2.yz, vec2(extent))) || any(lessThan(test2.yz, vec2(-extent))))
t2 = 0.;
if (t3 < 0. || any(greaterThan(test3.xz, vec2(extent))) || any(lessThan(test3.xz, vec2(-extent))))
t3 = 0.;
if (t4 < 0. || any(greaterThan(test4.xz, vec2(extent))) || any(lessThan(test4.xz, vec2(-extent))))
t4 = 0.;
if (t5 < 0. || any(greaterThan(test5.xy, vec2(extent))) || any(lessThan(test5.xy, vec2(-extent))))
t5 = 0.;
if (t6 < 0. || any(greaterThan(test6.xy, vec2(extent))) || any(lessThan(test6.xy, vec2(-extent))))
t6 = 0.;
// Initialize tmin and tmax values that define the two intersection points
tmin = 9999999999.;
tmax = 0.;
// Use the lowest value of t that is not 0 for tmin
if (t1 > 0. && t1 < tmin) { tmin = t1; minAxis = vec3(1, 0, 0); }
if (t2 > 0. && t2 < tmin) { tmin = t2; minAxis = vec3(1, 0, 0); }
if (t3 > 0. && t3 < tmin) { tmin = t3; minAxis = vec3(0, 1, 0); }
if (t4 > 0. && t4 < tmin) { tmin = t4; minAxis = vec3(0, 1, 0); }
if (t5 > 0. && t5 < tmin) { tmin = t5; minAxis = vec3(0, 0, 1); }
if (t6 > 0. && t6 < tmin) { tmin = t6; minAxis = vec3(0, 0, 1); }
// Use the highest value of t that is not 0 for tmax
if (t1 > 0. && t1 > tmax) { tmax = t1; maxAxis = vec3(1, 0, 0); }
if (t2 > 0. && t2 > tmax) { tmax = t2; maxAxis = vec3(1, 0, 0); }
if (t3 > 0. && t3 > tmax) { tmax = t3; maxAxis = vec3(0, 1, 0); }
if (t4 > 0. && t4 > tmax) { tmax = t4; maxAxis = vec3(0, 1, 0); }
if (t5 > 0. && t5 > tmax) { tmax = t5; maxAxis = vec3(0, 0, 1); }
if (t6 > 0. && t6 > tmax) { tmax = t6; maxAxis = vec3(0, 0, 1); }
// If tmin = tmax, the ray origin is within the cube, so set tmin to 0
if (tmin == tmax)
tmin = 0.;
// If tmax is not 0, an intersection was found
return tmax > 0.;
}
// Assuming the ray hits the given cube, calculates the time at which it hits it
void rayCubeExit(in Ray ray, in vec3 center, in float extent, out float t, out vec3 axis)
{
// Edges taking into account the direction of the ray
vec3 voxelEdges = center + (extent * ray.dirSign);
// Calculate the times at which the planes that span on the edges of the cubes are hit
vec3 axisTimes = (voxelEdges - ray.p0) * ray.invDir;
// The smallest of these times is side of the cube the ray hits (assuming it hits the cube)
t = min(min(axisTimes.x, axisTimes.y), axisTimes.z);
if (axisTimes.x == t) axis = vec3(1, 0, 0);
else if (axisTimes.y == t) axis = vec3(0, 1, 0);
else axis = vec3(0, 0, 1);
}
ivec3 wrapNodePointer(in ivec3 samplePos)
{
// Make sure that the indices are wrapped if the pointer doesn't fit in the current texture
if (samplePos.x >= texSize.x)
{
samplePos.x = samplePos.x % texSize.x;
samplePos.y++;
if (samplePos.y >= texSize.y)
{
samplePos.y = samplePos.y % texSize.y;
samplePos.z++;
}
//return vec4(255, 255, 0, float($max_level$ + 1));
}
return samplePos;
}
//************************************
// Given a pointer to a node, and it's childmask, and some child index
// returns the nodePointer to the child at the given index.
// Note that if the child doesn't exist, it still returns some pointer as if it would.
//************************************
uvec3 getNodePointer(uvec3 curNodePointer, uint childMask, uint childIndex)
{
// Based on the childIndex and the childmask, find out what the index of the childpointer is
int childPointerIndex = bitCount(childMask << (31 - childIndex));
// Find sample position in 3D texture corresponding to current node and locat
ivec3 samplePos = ivec3(curNodePointer);
samplePos.x += childPointerIndex;
// Make sure that the indices are wrapped if the pointer doesn't fit in the current texture
samplePos = wrapNodePointer(samplePos);
// Fetch the pointer from the texture
return texelFetch(octreeSampler, samplePos, 0).rgb;
}
// Fetches the childmask of the node at curNodePointer (note that this is not a mask for leaf nodes!)
uint getChildMask(in uvec3 nodePointer)
{
return texelFetch(octreeSampler, ivec3(nodePointer), 0).b;
}
uvec3 getColor(in uvec3 nodePointer)
{
return texelFetch(octreeSampler, ivec3(nodePointer), 0).rgb;
}
bool hasChild(in uint childMask, in uint childIndex)
{
uint mask = 1 << childIndex;
return (childMask & mask) == mask;
}
ivec3 getNextULoc(in Ray ray, in float t, in ivec3 lastULoc, in vec3 hitAxis)
{
vec3 samplePoint = ray.p0 + t * ray.dir/* + 0.005 * ray.dirSign*/;
vec3 hitDirMask = ray.dirSign * hitAxis;
// Calculate the location of the first leaf node that the ray enters
// HACK: Cast to int, and then to uint, because casting int to uint gets uint.maxValue if the value == -1,
// but float to uint directly rounds and gives 0, meaning you will never get out of the cube!
ivec3 nextULoc = ivec3(floor(samplePoint + hitDirMask));
return ivec3(ray.dirSign) * max(ivec3(ray.dirSign) * nextULoc, ivec3(ray.dirSign) * lastULoc);
}
uint getChildIndex(uvec3 uLoc, uint level)
{
uint range = 1 << ($max_level$ - level);
return int((uLoc.x & range) == range) * 1
+ int((uLoc.y & range) == range) * 2
+ int((uLoc.z & range) == range) * 4;
}
//****************************************
// Gets the position and scale (extent) of the child voxel at the given index
//****************************************
vec3 getChildPosition(vec3 parentPos, float childExtent, uint childIndex)
{
vec3 childOffset = vec3(
((childIndex & 1) == 1) ? childExtent : -childExtent,
((childIndex & 2) == 2) ? childExtent : -childExtent,
((childIndex & 4) == 4) ? childExtent : -childExtent);
return parentPos + childOffset;
}
//***********************************************
// Casts a ray against the octree structure, which is assumed to be located at rootcenter, with leaf voxels a size of 1, 1, 1
// Returns 0 if no intersection, 1 if intersection, and -1 if it is unknown if there was an intersection due to the maximum loop being reached
//***********************************************
int castRay(in Ray ray, in uint maxLoop, out float t, out uvec3 hitNodePointer, out vec3 hitAxis)
{
// Initialize out values
t = 0;
hitNodePointer = uvec3(0);
hitAxis = vec3(0);
// Initialize the stack
stackPos = 0;
for (int i = 0; i <= $max_level$; i++)
{
pointerStack[i] = uvec3(0); // The root node is always at position 0, 0, 0 in the texture
positionStack[i] = rootCenter;
extentStack[i] = float(1 << ($max_level$ - i + 1)) * 0.5;
}
// Find out if the ray intersects with the root node
float tminRoot, tmaxRoot;
vec3 minAxis, maxAxis;
bool collision = rayCube(ray, rootCenter, rootExtent, tminRoot, tmaxRoot, minAxis, maxAxis);
if (!collision) // Ray doesn't intersect with root.
return 0;
// Calculate the location of the first leaf node that the ray enters
ivec3 uLoc = getNextULoc(ray, tminRoot, ivec3(ray.p0), minAxis);
// Keep testing ray box intersection, but prevent endless looping
int loop = 0;
uvec3 rangeU = uvec3(range);
while (stackPos < $max_level$ && loop < maxLoop) {
uint preferredChild = getChildIndex(uLoc, stackPos);
float preferredChildExtent = extentStack[stackPos + 1];
vec3 preferredChildPosition = getChildPosition(positionStack[stackPos], preferredChildExtent, preferredChild);
uint childMask = getChildMask(pointerStack[stackPos]);
//************************************
// PUSH
// If we have the preferred child, explore it
//************************************
if (hasChild(childMask, preferredChild))
{
pointerStack[stackPos + 1] = getNodePointer(pointerStack[stackPos], childMask, preferredChild);
positionStack[stackPos + 1] = preferredChildPosition;
stackPos++;
}
//*************************************
// ADVANCE/POP
// If we didn't have the preferred child, find the next preferred child.
//*************************************
else
{
// Find the first child that the ray enters after it exits the current preferred child
rayCubeExit(ray, preferredChildPosition, preferredChildExtent, t, maxAxis);
ivec3 lastULoc = uLoc;
uLoc = getNextULoc(ray, t - 0.05, lastULoc, maxAxis);
if (any(lessThan(uLoc, uvec3(0))) || any(greaterThanEqual(uLoc, rangeU)))
return 0;
// Count the number of bits that were the same in the last uLoc (until they start not being the same)
// Since lastULoc is in the current node, we know that this number of bits is the last common parent, so we pop up to that level.
// Note that this level is indeed one higher, but we need that to advance to the next cell?
ivec3 equalBits = $max_level$ - findMSB(lastULoc ^ uLoc);
stackPos = min(min(equalBits.x, equalBits.y), equalBits.z);
}
++loop;
}
hitAxis = maxAxis;
hitNodePointer = pointerStack[stackPos];
// Check if the current node is a leaf node. If so, draw it:
if (stackPos >= $max_level$)
return 1;
return -1;
}
void main() {
uint maxLoop = 4096;
texSize = textureSize(octreeSampler, 0);
// Set color using textures
color = texture(textureSampler, uv);
// Scale the grid so that all leaf cells have a width of exactly 1:
float extent = $extent$;
float rangeF = float(range);
float scale = rangeF / (extent * 2.);
float offset = extent + 0.5 / scale;
// Get ray and sample point
vec3 p0 = (cameraPosition + offset) * scale;
vec3 rayDir = normalize(pos - cameraPosition);
Ray ray = getRay(p0, rayDir);
// Cast the primary ray
float time;
vec3 hitAxis;
uvec3 hitNodePointer;
int primaryRayHit = castRay(ray, maxLoop, time, hitNodePointer, hitAxis);
if (primaryRayHit == 1)
{
color.rgb = vec3(getColor(hitNodePointer)) / 255.;
// Cast one shadow ray
if (lightDirection != vec3(0))
{
Ray lightRay = getRay(ray.dir * time + p0 - ray.dir * 0.01, -lightDirection);
int shadowResult = castRay(lightRay, max(range / 1024, 1000), time, hitNodePointer, hitAxis);
if (shadowResult == 1 || shadowResult == -1)
color.rgb *= 0.5 + (0.5 - float(abs(shadowResult)) * 0.5);
}
}
// Discard pixels that are transparent
if (color.a < 0.5)
discard;
}

View File

@@ -0,0 +1,22 @@
shader_vertexPosition 0
shader_vertexUV 1
shader_vertexNormal 2
shader_vertexColor 3
shader_MVP 0
shader_width 1
shader_height 2
shader_angle 3
shader_aspect 4
shader_lightDirection 5
shader_lightType 6
shader_cameraPosition 7
shader_reflections 8
shader_pixelHalfConeAngle 9
shader_bitsPerChannel 10
shader_bitsPerTree 11
shader_invMVP 12
shader_textColor 0
shader_max_level 12
shader_extent 600.0