diff --git a/shader/include/constants.glsl b/shader/include/constants.glsl index 4c23836..7892915 100644 --- a/shader/include/constants.glsl +++ b/shader/include/constants.glsl @@ -1,2 +1,4 @@ -const float INF = 1000.0; +const float INF = 17.0; const float PI = 3.14159; +const int SAMPLES = 1; +const int BOUNCES = 4; diff --git a/shader/include/image.glsl b/shader/include/image.glsl index 904b14d..c212f2d 100644 --- a/shader/include/image.glsl +++ b/shader/include/image.glsl @@ -1,9 +1,5 @@ -vec2 pixelUv() +vec2 pixelUv(ivec2 pixelCoords, ivec2 dims) { - 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); diff --git a/shader/include/lighting.glsl b/shader/include/lighting.glsl index aad8d78..a422a04 100644 --- a/shader/include/lighting.glsl +++ b/shader/include/lighting.glsl @@ -12,7 +12,11 @@ mat3 getTangentSpace(vec3 normal) vec3 sampleHemisphere(vec3 normal) { - vec2 uv = pixelUv(); + // TODO: make independent of this image uniform + ivec2 pixelCoords = ivec2(gl_GlobalInvocationID.xy); + ivec2 dims = imageSize(img_output); + + vec2 uv = pixelUv(pixelCoords, dims); uv += _seed.xy; vec4 noise = sampleNoise(uv);; diff --git a/shader/root/rt.glsl b/shader/root/rt.glsl index fc3ca94..77362e7 100644 --- a/shader/root/rt.glsl +++ b/shader/root/rt.glsl @@ -2,6 +2,9 @@ layout(local_size_x = 1, local_size_y = 1) in; // size of local work group - 1 pixel +// gbuffer? +layout(rgba32f, binding = 2) readonly uniform image2D _g0; + // final output layout(rgba32f, binding = 0) uniform image2D img_output; // rgba32f defines internal format, image2d for random write to output texture @@ -35,18 +38,18 @@ void main() // base pixel colour for the image vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0); - vec2 uv = pixelUv(); + ivec2 pixelCoords = ivec2(gl_GlobalInvocationID.xy); + ivec2 dims = imageSize(img_output); - int samples = 1; - int bounces = 4; + vec2 uv = pixelUv(pixelCoords, dims); - for (int i = 0; i < samples; i++) + for (int i = 0; i < SAMPLES; i++) { // create a ray from the uv Ray ray = createCameraRay(uv); // trace the rays path around the scene - for (int j = 0; j < bounces; j++) + for (int j = 0; j < BOUNCES; j++) { RayHit hit = trace(ray); @@ -55,7 +58,16 @@ void main() if (length(ray.energy) < 0.001) break; } } - pixel.xyz /= samples; + pixel.xyz /= SAMPLES; + + vec4 d = imageLoad(_g0, ivec2(gl_GlobalInvocationID.xy)); + float depth = d.x; + + pixel.xyz = mix(pixel.xyz, vec3(0), depth); + + //pixel.a = 1.0; + + //pixel.xyz = texture(_g0, uv).xyz; // output to a specific pixel in the image imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), pixel); diff --git a/shader/root/rtpre.glsl b/shader/root/rtpre.glsl index 2dc79bc..dfb9019 100644 --- a/shader/root/rtpre.glsl +++ b/shader/root/rtpre.glsl @@ -1,3 +1,48 @@ #version 430 +// writes first hit information into buffers to accelerate later passes + +// local work group +layout(local_size_x = 1, local_size_y = 1) in; + +layout (rgba32f, binding = 2) uniform image2D g0_output; + +#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 +#include scene.glsl + +void main() +{ + // x normal.x + // y normal.y + // z normal.z + // w depth + + vec4 pixel= vec4(0); + ivec2 pixelCoords = ivec2(gl_GlobalInvocationID.xy); + ivec2 dims = imageSize(g0_output); + vec2 uv = pixelUv(pixelCoords, dims); + + Ray ray = createCameraRay(uv); + RayHit hit = trace(ray); + + //pixel.xyz = hit.normal; + + // TODO: non-linear depth + float n = 1; + float f = INF; + float z = hit.distance; + + float depth = (1.0/z-1.0/n)/(1.0/n-1.0/f); + + pixel.x = z/f; + + imageStore(g0_output, pixelCoords, pixel); +} diff --git a/src/gfx.c b/src/gfx.c index 574aa1f..3346542 100644 --- a/src/gfx.c +++ b/src/gfx.c @@ -14,11 +14,8 @@ unsigned int indices[] = { 1, 2, 3 }; -SDL_Window* sdlWindow; SDL_GLContext* sdlContext; -GLuint compileShader(const char* path, GLenum type); - SDL_Window* gfxInit(int width, int height) { // load sdl modules @@ -33,7 +30,7 @@ SDL_Window* gfxInit(int width, int height) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); - sdlWindow = SDL_CreateWindow( + SDL_Window *sdlWindow = SDL_CreateWindow( "oglc", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, @@ -62,19 +59,36 @@ SDL_Window* gfxInit(int width, int height) glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &imageUnits); printf("max texture image units %d\n", imageUnits); + printWorkGroupLimits(); + return sdlWindow; } -unsigned int compileQuadShaderProgram(const char* vsPath, const char* fsPath) +int compileShaders(struct Shaders *shaders) +{ + // everything is renderered onto the surface of one quad + shaders->quad = compileQuadShaderProgram("bin/shader.vert", "bin/shader.frag"); + + // preprass writes first-pass information into buffers for later acceleration + shaders->prepass = compileComputeShaderProgram("bin/rtpre.compute"); + + // lighting traces multiple bounces of light around the scene + shaders->lighting = compileComputeShaderProgram("bin/rt.compute"); + + // TODO: actually make sure all the shaders compile + return 0; +} + +GLuint compileQuadShaderProgram(const char* vsPath, const char* fsPath) { GLuint vs = compileShader(vsPath, GL_VERTEX_SHADER); GLuint fs = compileShader(fsPath, GL_FRAGMENT_SHADER); - unsigned int shaderProgram = glCreateProgram(); + GLuint shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vs); glAttachShader(shaderProgram, fs); - // TODO: check program linking success + // TODO: check program linking success textur glLinkProgram(shaderProgram); GLint result = GL_FALSE; @@ -99,7 +113,7 @@ unsigned int compileQuadShaderProgram(const char* vsPath, const char* fsPath) return shaderProgram; } -unsigned int compileComputeShaderProgram(const char* computeShaderPath) +GLuint compileComputeShaderProgram(const char* computeShaderPath) { GLuint cs = compileShader(computeShaderPath, GL_COMPUTE_SHADER); GLuint computeProgram = glCreateProgram(); @@ -160,15 +174,40 @@ void setVertexAttributes() // color glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, (void*)(3*sizeof(float))); glEnableVertexAttribArray(1); + + + + + + + + + + // uv glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, stride, (void*)(6*sizeof(float))); glEnableVertexAttribArray(2); } +int createTextures(int width, int height, struct Shaders shaders, struct Textures* textures) +{ + // generate noise + textures->noise = createNoiseTexture(width, height); + glBindTexture(GL_TEXTURE_2D,textures->noise); + int loc = glGetUniformLocation(shaders.lighting, "_noise"); + glUniform1i(loc, textures->noise); + + // create a texture for the compute shader to write to + textures->target = createWriteOnlyTexture(width, height); + + textures->g0 = createTexture(width, height); + + return 0; +} + // creates a noise texture in active texture 1 GLuint createNoiseTexture(int width, int height) { - // same init steps as with a regular texture GLuint texture; glGenTextures(1, &texture); @@ -216,9 +255,27 @@ GLuint createWriteOnlyTexture(int width, int height) return texture; } +// creates an empty texture in GL_TEXTURE2 unit +GLuint createTexture(int width, int height) +{ + GLuint texture; + glGenTextures(1, &texture); + //glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // an empty image + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, width, height, 0, GL_RGBA, GL_FLOAT, NULL); + glBindImageTexture(2, texture, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); + + return texture; +} + void initBuffers() { - unsigned int VAO, VBO, EBO; + GLuint VAO, VBO, EBO; glGenVertexArrays(1, &VAO); // vertex array object glGenBuffers(1, &VBO); // vertex buffer object glGenBuffers(1, &EBO); // element buffer object diff --git a/src/gfx.h b/src/gfx.h index 95d11c1..78e2827 100644 --- a/src/gfx.h +++ b/src/gfx.h @@ -9,15 +9,46 @@ #include "io.h" #include "random.h" +struct Shaders +{ + GLuint quad; + GLuint prepass; + GLuint lighting; +}; + +struct Textures +{ + // texture that ultimately gets drawn to the framebuffer + GLuint target; + + // TODO: blue noise, pink noise! + // noise texture to help with randomness + GLuint noise; + + // additional view space information + // + // x depth + // y - + // z - + // w - + GLuint g0; +}; + SDL_Window* gfxInit(int width, int height); -unsigned int compileQuadShaderProgram(const char* vsPath, const char* fsPath); -unsigned int compileComputeShaderProgram(); +int compileShaders(struct Shaders *shaders); +GLuint compileQuadShaderProgram(const char* vsPath, const char* fsPath); +GLuint compileComputeShaderProgram(const char* csPath); +int createTextures(int width, int height, struct Shaders shaders, struct Textures* textures); GLuint createNoiseTexture(int width, int height); +GLuint createTexture(int width, int height); GLuint createWriteOnlyTexture(int width, int height); +GLuint compileShader(const char* path, GLenum type); + void printWorkGroupLimits(); +// quad initialisation void setVertexAttributes(); void initBuffers(); diff --git a/src/main.c b/src/main.c index c2f90af..ad29035 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,4 @@ + #include "main.h" #include "gfx.h" #include "clock.h" @@ -5,72 +6,76 @@ #include "sphere.h" #include "cam.h" +#include "input.h" const int WIDTH = 420; const int HEIGHT = 420; void updateUniforms(GLuint shaderProgram); -struct TextureIDs -{ - GLuint output; // the texture that ultimately gets rendered - GLuint noise; -} textureIds; +SDL_Window *window; -int main() +struct Shaders shaders; +struct Textures textures; + +void initialise() { - printf("GL_TEXTURE0: %d\n", GL_TEXTURE0); - printf("GL_TEXTURE1: %d\n", GL_TEXTURE1); + window = gfxInit(WIDTH, HEIGHT); randomInit(); - // create a window and opengl context - SDL_Window* window = gfxInit(WIDTH, HEIGHT); - - // compile shader programs - unsigned int computeProgram = compileComputeShaderProgram( - "bin/rt.compute"); - unsigned int quadProgram = compileQuadShaderProgram( - "bin/shader.vert", - "bin/shader.frag"); - - // generate noise - textureIds.noise = createNoiseTexture(WIDTH, HEIGHT); - glBindTexture(GL_TEXTURE_2D,textureIds.noise); - int noiseLoc = glGetUniformLocation(computeProgram, "_noise"); - glUniform1i(noiseLoc, textureIds.noise); - - // create a texture for the compute shader to write to - textureIds.output = createWriteOnlyTexture(WIDTH, HEIGHT); - printWorkGroupLimits(); - // initialise quad initBuffers(); setVertexAttributes(); - int frames = 0; - // render loop - while (!checkQuit()) + // compile shader programs + compileShaders(&shaders); + createTextures(WIDTH, HEIGHT, shaders, &textures); +} + +int main() +{ + initialise(); + + int frames; + for (frames = 0; !checkQuit(); frames++) { - // dispatch compute shader - glUseProgram(computeProgram); - updateUniforms(computeProgram); + GLuint shader; + + // prepass + // TODO: write output to different texture than main output + shader = shaders.prepass; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textures.g0); + glUseProgram(shader); + updateUniforms(shader); + glDispatchCompute((GLuint)WIDTH, (GLuint)HEIGHT, 1); + + // make sure we're finished writing to the texture before trying to read it + glMemoryBarrier(GL_ALL_BARRIER_BITS); + + // dispatch compute shaders + shader = shaders.lighting; + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, textures.target); + glUseProgram(shader); + updateUniforms(shader); + //int loc = glGetUniformLocation(shader, "_g0"); + //glUniform1i(loc, textures.g0); glDispatchCompute((GLuint)WIDTH, (GLuint)HEIGHT, 1); // make sure we're finished writing to the texture before trying to read it glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); // normal drawing pass - glUseProgram(quadProgram); + shader = shaders.quad; + glUseProgram(shader); // bind texture written to by compute stage to 2d target - glBindTexture(GL_TEXTURE_2D, textureIds.output); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // swip swap SDL_GL_SwapWindow(window); - - frames++; } float elapsed = now();