From c4b74458a80cb10bc51401f8f90e1bec0ec8dd58 Mon Sep 17 00:00:00 2001 From: ktyl Date: Tue, 10 Aug 2021 01:11:22 +0100 Subject: [PATCH] extract includes --- shader/include/camera.glsl | 28 ++++ shader/include/constants.glsl | 2 + shader/include/func.glsl | 4 + shader/include/image.glsl | 12 ++ shader/include/intersect.glsl | 39 ++++++ shader/include/lighting.glsl | 39 ++++++ shader/include/random.glsl | 14 ++ shader/include/ray.glsl | 36 +++++ shader/include/scene.glsl | 17 +++ shader/include/sphere.glsl | 5 + shader/include/time.glsl | 1 + shader/root/rt.glsl | 245 +++------------------------------- shader/root/rtpre.glsl | 3 + src/main.c | 26 ++-- todo.md | 1 + 15 files changed, 232 insertions(+), 240 deletions(-) create mode 100644 shader/include/camera.glsl create mode 100644 shader/include/constants.glsl create mode 100644 shader/include/func.glsl create mode 100644 shader/include/image.glsl create mode 100644 shader/include/intersect.glsl create mode 100644 shader/include/lighting.glsl create mode 100644 shader/include/random.glsl create mode 100644 shader/include/ray.glsl create mode 100644 shader/include/scene.glsl create mode 100644 shader/include/time.glsl create mode 100644 shader/root/rtpre.glsl diff --git a/shader/include/camera.glsl b/shader/include/camera.glsl new file mode 100644 index 0000000..7f66ee4 --- /dev/null +++ b/shader/include/camera.glsl @@ -0,0 +1,28 @@ +// view space axes +uniform vec3 _w; +uniform vec3 _u; +uniform vec3 _v; + +uniform mat4 _cameraInverseProjection; +uniform vec3 _camh; +uniform vec3 _camv; +uniform vec3 _camll; +uniform vec3 _cpos; +uniform vec3 _tpos; + +Ray createCameraRay(vec2 uv) +{ + // transform -1..1 -> 0..1 + uv = uv*0.5+0.5; + + vec3 target = vec3(0,0,0); + + vec3 dir; + dir = uv.x*_camh + uv.y*_camv; + dir = _camll + uv.x*_camh + uv.y*_camv; + dir = normalize(dir); + + Ray ray = createRay(_cpos, dir); + + return ray; +}; diff --git a/shader/include/constants.glsl b/shader/include/constants.glsl new file mode 100644 index 0000000..4c23836 --- /dev/null +++ b/shader/include/constants.glsl @@ -0,0 +1,2 @@ +const float INF = 1000.0; +const float PI = 3.14159; diff --git a/shader/include/func.glsl b/shader/include/func.glsl new file mode 100644 index 0000000..4c0a62b --- /dev/null +++ b/shader/include/func.glsl @@ -0,0 +1,4 @@ +float sdot(vec3 x, vec3 y, float f = 1.0) +{ + return clamp(dot(x,y)*f,0.0,1.0); +} diff --git a/shader/include/image.glsl b/shader/include/image.glsl new file mode 100644 index 0000000..904b14d --- /dev/null +++ b/shader/include/image.glsl @@ -0,0 +1,12 @@ +vec2 pixelUv() +{ + ivec2 pixelCoords = ivec2(gl_GlobalInvocationID.xy); + + ivec2 dims = imageSize(img_output); + + vec2 uv; + uv.x = (float(pixelCoords.x * 2 - dims.x) / dims.x) * dims.x/dims.y; // account for aspect ratio + uv.y = (float(pixelCoords.y * 2 - dims.y) / dims.y); + + return uv; +} diff --git a/shader/include/intersect.glsl b/shader/include/intersect.glsl new file mode 100644 index 0000000..b6743f0 --- /dev/null +++ b/shader/include/intersect.glsl @@ -0,0 +1,39 @@ +void intersectSphere(Ray ray, inout RayHit bestHit, Sphere sphere) +{ + vec3 c = sphere.cr.xyz; + float r = sphere.cr.w; + + vec3 d = ray.origin-c; + float p1 = -dot(ray.direction,d); + float p2sqr = p1*p1-dot(d,d)+r*r; + + if (p2sqr < 0) return; + + float p2 = sqrt(p2sqr); + float t = p1-p2 > 0 ? p1-p2 : p1+p2; + if (t > 0 && t < bestHit.distance) + { + bestHit.distance = t; + bestHit.position = ray.origin + t * ray.direction; + bestHit.normal = normalize(bestHit.position-c); + bestHit.albedo = sphere.albedo; + } +} + +void intersectPlane(Ray ray, inout RayHit bestHit, vec3 p, vec3 normal) +{ + //normal = vec3(0.0,0.0,1.0); + float denom = dot(normal, ray.direction); + + if (abs(denom) > 0.0001) + { + float t = dot(p-ray.origin, normal) / denom; + if (t >= 0 && t < bestHit.distance) + { + bestHit.distance = t; + bestHit.position = ray.origin + t*ray.direction; + bestHit.normal = normal; + bestHit.albedo = vec3(1.0,1.0,1.0); + } + } +} diff --git a/shader/include/lighting.glsl b/shader/include/lighting.glsl new file mode 100644 index 0000000..aad8d78 --- /dev/null +++ b/shader/include/lighting.glsl @@ -0,0 +1,39 @@ +mat3 getTangentSpace(vec3 normal) +{ + vec3 helper = abs(normal.x) > 0.99 + ? vec3(1.0,0.0,0.0) + : vec3(0.0,0.0,1.0); + + vec3 tangent = normalize(cross(normal, helper)); + vec3 binormal = normalize(cross(normal, tangent)); + + return mat3(tangent, binormal, normal); +} + +vec3 sampleHemisphere(vec3 normal) +{ + vec2 uv = pixelUv(); + uv += _seed.xy; + + vec4 noise = sampleNoise(uv);; + + float cosTheta = random(normalize(normal.xy+noise.xy)); + float sinTheta = sqrt(max(0.0,1.0-cosTheta*cosTheta)); + + float phi = 2.0*PI*random(normalize(normal.yz+noise.xw)); + vec3 tangentSpaceDir = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta); + + // convert direction from tangent space to world space + mat3 ts = getTangentSpace(normal); + return ts * tangentSpaceDir; +} + + +vec3 scatterLambert(inout Ray ray, RayHit hit) +{ + ray.origin = hit.position + hit.normal*0.001; + ray.direction = sampleHemisphere(hit.normal); + ray.energy *= 2.0 * hit.albedo * sdot(hit.normal, ray.direction); + + return vec3(0.0); +} diff --git a/shader/include/random.glsl b/shader/include/random.glsl new file mode 100644 index 0000000..8226cd1 --- /dev/null +++ b/shader/include/random.glsl @@ -0,0 +1,14 @@ +uniform vec4 _seed; +layout(binding=1) uniform sampler2D _noise; // noise texture + +vec4 sampleNoise(vec2 st) +{ + return texture(_noise, st); +} + +float random(vec2 st) +{ + vec2 nuv = sampleNoise(st.xy).xy; + + return fract(sin(dot(nuv,vec2(12.9898,78.233)))*43758.5453123); +} diff --git a/shader/include/ray.glsl b/shader/include/ray.glsl new file mode 100644 index 0000000..f404efd --- /dev/null +++ b/shader/include/ray.glsl @@ -0,0 +1,36 @@ +struct Ray +{ + vec3 origin; + vec3 direction; + vec3 energy; +}; +Ray createRay(vec3 origin, vec3 direction) +{ + Ray ray; + + ray.origin = origin; + ray.direction = direction; + ray.energy = vec3(1.0,1.0,1.0); + + return ray; +} + +struct RayHit +{ + vec3 position; + float distance; + vec3 normal; + vec3 albedo; +}; +RayHit createRayHit() +{ + RayHit hit; + + hit.position = vec3(0.0,0.0,0.0); + // TODO: this might not be defined + hit.distance = INF; + hit.normal = vec3(0.0,0.0,0.0); + hit.albedo = vec3(0.0,0.0,0.0); + + return hit; +} diff --git a/shader/include/scene.glsl b/shader/include/scene.glsl new file mode 100644 index 0000000..c821a99 --- /dev/null +++ b/shader/include/scene.glsl @@ -0,0 +1,17 @@ +RayHit trace(Ray ray) +{ + RayHit hit = createRayHit(); + + Sphere s; + s.cr = vec4(0.0,0.0,0.0,2.0); + s.albedo = vec3(1.0,0.0,0.0); + + intersectPlane(ray, hit, vec3(0.0,-1.5,0.0),vec3(0.0,1.0,0.0)); + + for (int i = 0; i < _activeSpheres; i++) + { + intersectSphere(ray, hit, _spheres[i]); + } + + return hit; +} diff --git a/shader/include/sphere.glsl b/shader/include/sphere.glsl index dfa7483..ec9f1e3 100644 --- a/shader/include/sphere.glsl +++ b/shader/include/sphere.glsl @@ -4,3 +4,8 @@ struct Sphere vec4 cr; vec3 albedo; }; + +// 253 is the maximum?? TODO: use uniform buffer objects +const int SPHERES = 250; +uniform int _activeSpheres; +layout (location = 1) uniform Sphere _spheres[SPHERES]; diff --git a/shader/include/time.glsl b/shader/include/time.glsl new file mode 100644 index 0000000..2a2dcc2 --- /dev/null +++ b/shader/include/time.glsl @@ -0,0 +1 @@ +uniform vec4 _t; diff --git a/shader/root/rt.glsl b/shader/root/rt.glsl index 8c53957..fc3ca94 100644 --- a/shader/root/rt.glsl +++ b/shader/root/rt.glsl @@ -1,226 +1,23 @@ #version 430 -#include sphere.glsl - layout(local_size_x = 1, local_size_y = 1) in; // size of local work group - 1 pixel -// TODO: do i actually need explicit location descriptors? -layout (location = 1) uniform vec4 _t; - -layout (location = 2) uniform vec3 _w; // view space axes -layout (location = 3) uniform vec3 _u; -layout (location = 4) uniform vec3 _v; - -layout (location = 5) uniform mat4 _cameraInverseProjection; -layout (location = 6) uniform vec3 _camh; -layout (location = 7) uniform vec3 _camv; -layout (location = 8) uniform vec3 _camll; -layout (location = 9) uniform vec3 _cpos; -layout (location = 10) uniform vec3 _tpos; // target - -// 253 is the maximum?? TODO: use uniform buffer objects -const int SPHERES = 250; - -layout (location = 12) uniform int _activeSpheres; -layout (location = 13) uniform Sphere _spheres[SPHERES]; - -uniform vec4 _seed; - +// final output layout(rgba32f, binding = 0) uniform image2D img_output; // rgba32f defines internal format, image2d for random write to output texture -layout(binding=1) uniform sampler2D _noise; // noise texture -const float INF = 1000.0; -const float PI = 3.14159; - -struct Ray -{ - vec3 origin; - vec3 direction; - vec3 energy; -}; -Ray createRay(vec3 origin, vec3 direction) -{ - Ray ray; - - ray.origin = origin; - ray.direction = direction; - ray.energy = vec3(1.0,1.0,1.0); - - return ray; -} - -struct RayHit -{ - vec3 position; - float distance; - vec3 normal; - vec3 albedo; -}; -RayHit createRayHit() -{ - RayHit hit; - - hit.position = vec3(0.0,0.0,0.0); - hit.distance = INF; - hit.normal = vec3(0.0,0.0,0.0); - hit.albedo = vec3(0.0,0.0,0.0); - - return hit; -} - -void intersectSphere(Ray ray, inout RayHit bestHit, Sphere sphere) -{ - vec3 c = sphere.cr.xyz; - float r = sphere.cr.w; - - vec3 d = ray.origin-c; - float p1 = -dot(ray.direction,d); - float p2sqr = p1*p1-dot(d,d)+r*r; - - if (p2sqr < 0) return; - - float p2 = sqrt(p2sqr); - float t = p1-p2 > 0 ? p1-p2 : p1+p2; - if (t > 0 && t < bestHit.distance) - { - bestHit.distance = t; - bestHit.position = ray.origin + t * ray.direction; - bestHit.normal = normalize(bestHit.position-c); - bestHit.albedo = sphere.albedo; - } -} - -void intersectPlane(Ray ray, inout RayHit bestHit, vec3 p, vec3 normal) -{ - //normal = vec3(0.0,0.0,1.0); - float denom = dot(normal, ray.direction); - - if (abs(denom) > 0.0001) - { - float t = dot(p-ray.origin, normal) / denom; - if (t >= 0 && t < bestHit.distance) - { - bestHit.distance = t; - bestHit.position = ray.origin + t*ray.direction; - bestHit.normal = normal; - bestHit.albedo = vec3(1.0,1.0,1.0); - } - } -} - -Ray createCameraRay(vec2 uv) -{ - // transform -1..1 -> 0..1 - uv = uv*0.5+0.5; - //uv.x=1-uv.x; - - vec3 target = vec3(0,0,0); - - vec3 dir; - dir = uv.x*_camh + uv.y*_camv; - dir = _camll + uv.x*_camh + uv.y*_camv; - dir = normalize(dir); - - Ray ray = createRay(_cpos, dir); - - return ray; -}; - -RayHit trace(Ray ray) -{ - RayHit hit = createRayHit(); - - // TODO: intersect something other than spheres - - Sphere s; - s.cr = vec4(0.0,0.0,0.0,2.0); - s.albedo = vec3(1.0,0.0,0.0); - - intersectPlane(ray, hit, vec3(0.0,-1.5,0.0),vec3(0.0,1.0,0.0)); - - for (int i = 0; i < _activeSpheres; i++) - { - intersectSphere(ray, hit, _spheres[i]); - } - - //intersectSphere(ray, hit, s); - - return hit; -} - -vec2 pixelUv() -{ - ivec2 pixelCoords = ivec2(gl_GlobalInvocationID.xy); - - ivec2 dims = imageSize(img_output); - - vec2 uv; - uv.x = (float(pixelCoords.x * 2 - dims.x) / dims.x) * dims.x/dims.y; // account for aspect ratio - uv.y = (float(pixelCoords.y * 2 - dims.y) / dims.y); - - return uv; -} - -vec4 sampleNoise() -{ - vec2 uv = pixelUv(); - uv += _seed.xy; - - return texture(_noise, uv); -} - -float random(vec2 st) -{ - //st += gl_GlobalInvocationID.xy; - //st += _seed.xy; - //st += _seed.zw; - //normalize(st); - - vec2 nuv = texture(_noise, st.xy).xy; - - return fract(sin(dot(nuv,vec2(12.9898,78.233)))*43758.5453123); -} - -float sdot(vec3 x, vec3 y, float f = 1.0) -{ - return clamp(dot(x,y)*f,0.0,1.0); -} - -mat3 getTangentSpace(vec3 normal) -{ - vec3 helper = abs(normal.x) > 0.99 - ? vec3(1.0,0.0,0.0) - : vec3(0.0,0.0,1.0); - - vec3 tangent = normalize(cross(normal, helper)); - vec3 binormal = normalize(cross(normal, tangent)); - - return mat3(tangent, binormal, normal); -} - -vec3 sampleHemisphere(vec3 normal) -{ - vec4 noise = sampleNoise(); - - float cosTheta = random(normalize(normal.xy+noise.xy)); - float sinTheta = sqrt(max(0.0,1.0-cosTheta*cosTheta)); - - float phi = 2.0*PI*random(normalize(normal.yz+noise.xw)); - vec3 tangentSpaceDir = vec3(cos(phi)*sinTheta, sin(phi)*sinTheta, cosTheta); - - // convert direction from tangent space to world space - mat3 ts = getTangentSpace(normal); - return ts * tangentSpaceDir; -} - -vec3 scatterLambert(inout Ray ray, RayHit hit) -{ - ray.origin = hit.position + hit.normal*0.001; - ray.direction = sampleHemisphere(hit.normal); - ray.energy *= 2.0 * hit.albedo * sdot(hit.normal, ray.direction); - - return vec3(0.0); -} +// TODO: some of these depend on each other!! need be be in this order for now c: +#include func.glsl +#include constants.glsl +#include time.glsl +#include sphere.glsl +#include ray.glsl +#include intersect.glsl +#include random.glsl +#include camera.glsl +#include image.glsl +// scene.glsl includes scene trace function +#include scene.glsl +#include lighting.glsl vec3 shade(inout Ray ray, RayHit hit) { @@ -229,8 +26,6 @@ vec3 shade(inout Ray ray, RayHit hit) return scatterLambert(ray, hit); } - //ray.energy = vec3(0.0); - // sky color return vec3(0.68,0.85,0.9); } @@ -239,8 +34,6 @@ void main() { // base pixel colour for the image vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0); - // get index in global work group ie xy position - ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy); vec2 uv = pixelUv(); @@ -262,16 +55,8 @@ void main() if (length(ray.energy) < 0.001) break; } } - pixel.xyz /= samples; - // TODO: write depth to texture - //float depth = hit.distance/INF; - //pixel = vec4(hit.albedo,1.0); - //pixel *= (1.0-depth); - - //pixel = texture(_noise, uv); - // output to a specific pixel in the image - imageStore(img_output, pixel_coords, pixel); + imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), pixel); } diff --git a/shader/root/rtpre.glsl b/shader/root/rtpre.glsl new file mode 100644 index 0000000..2dc79bc --- /dev/null +++ b/shader/root/rtpre.glsl @@ -0,0 +1,3 @@ +#version 430 + +#include sphere.glsl diff --git a/src/main.c b/src/main.c index 77651fe..c2f90af 100644 --- a/src/main.c +++ b/src/main.c @@ -11,6 +11,12 @@ const int HEIGHT = 420; void updateUniforms(GLuint shaderProgram); +struct TextureIDs +{ + GLuint output; // the texture that ultimately gets rendered + GLuint noise; +} textureIds; + int main() { printf("GL_TEXTURE0: %d\n", GL_TEXTURE0); @@ -21,8 +27,6 @@ int main() // create a window and opengl context SDL_Window* window = gfxInit(WIDTH, HEIGHT); - - // compile shader programs unsigned int computeProgram = compileComputeShaderProgram( "bin/rt.compute"); @@ -31,13 +35,13 @@ int main() "bin/shader.frag"); // generate noise - GLuint noise = createNoiseTexture(WIDTH, HEIGHT); - glBindTexture(GL_TEXTURE_2D, noise); + textureIds.noise = createNoiseTexture(WIDTH, HEIGHT); + glBindTexture(GL_TEXTURE_2D,textureIds.noise); int noiseLoc = glGetUniformLocation(computeProgram, "_noise"); - glUniform1i(noiseLoc, noise); + glUniform1i(noiseLoc, textureIds.noise); // create a texture for the compute shader to write to - GLuint textureOutput = createWriteOnlyTexture(WIDTH, HEIGHT); + textureIds.output = createWriteOnlyTexture(WIDTH, HEIGHT); printWorkGroupLimits(); // initialise quad @@ -60,7 +64,7 @@ int main() glUseProgram(quadProgram); // bind texture written to by compute stage to 2d target - glBindTexture(GL_TEXTURE_2D, textureOutput); + glBindTexture(GL_TEXTURE_2D, textureIds.output); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // swip swap @@ -91,17 +95,19 @@ void updateUniforms(GLuint shaderProgram) int loc = glGetUniformLocation(shaderProgram, "_seed"); glUniform4fv(loc, 1, seed); + // update time float t = now(); float sin_t = sin(t); - int tLocation = glGetUniformLocation(shaderProgram, "_t"); - glUniform4f(tLocation, t, sin_t, (1.0 + sin_t)*0.5, 0.0f); + loc = glGetUniformLocation(shaderProgram, "_t"); + glUniform4f(loc, t, sin_t, (1.0 + sin_t)*0.5, 0.0f); + // update camera float aspect = (float)WIDTH/(float)HEIGHT; updateCameraUniforms(shaderProgram, aspect); + // make and update spheres const int sphereCount = 42; struct Sphere spheres[sphereCount]; makeSpheres(spheres, sphereCount); - updateSphereUniforms(shaderProgram, spheres, sphereCount); } diff --git a/todo.md b/todo.md index 2c7e46c..124baf3 100644 --- a/todo.md +++ b/todo.md @@ -1,6 +1,7 @@ * [-] preprocessor * [x] #include directives * [ ] keep track of previously included files to avoid redefinitions + * [ ] include as submodule * [-] render image with compute shader * [x] render a texture to a full-screen quad * [x] pass uniforms to shader to animate it