refactor shaders, implement depth buffer

This commit is contained in:
ktyl 2021-08-12 21:16:45 +01:00
parent c4b74458a8
commit cdc4aea9d5
8 changed files with 215 additions and 63 deletions

View File

@ -1,2 +1,4 @@
const float INF = 1000.0; const float INF = 17.0;
const float PI = 3.14159; const float PI = 3.14159;
const int SAMPLES = 1;
const int BOUNCES = 4;

View File

@ -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; vec2 uv;
uv.x = (float(pixelCoords.x * 2 - dims.x) / dims.x) * dims.x/dims.y; // account for aspect ratio 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); uv.y = (float(pixelCoords.y * 2 - dims.y) / dims.y);

View File

@ -12,7 +12,11 @@ mat3 getTangentSpace(vec3 normal)
vec3 sampleHemisphere(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; uv += _seed.xy;
vec4 noise = sampleNoise(uv);; vec4 noise = sampleNoise(uv);;

View File

@ -2,6 +2,9 @@
layout(local_size_x = 1, local_size_y = 1) in; // size of local work group - 1 pixel 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 // final output
layout(rgba32f, binding = 0) uniform image2D img_output; // rgba32f defines internal format, image2d for random write to output texture 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 // base pixel colour for the image
vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0); 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; vec2 uv = pixelUv(pixelCoords, dims);
int bounces = 4;
for (int i = 0; i < samples; i++) for (int i = 0; i < SAMPLES; i++)
{ {
// create a ray from the uv // create a ray from the uv
Ray ray = createCameraRay(uv); Ray ray = createCameraRay(uv);
// trace the rays path around the scene // 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); RayHit hit = trace(ray);
@ -55,7 +58,16 @@ void main()
if (length(ray.energy) < 0.001) break; 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 // output to a specific pixel in the image
imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), pixel); imageStore(img_output, ivec2(gl_GlobalInvocationID.xy), pixel);

View File

@ -1,3 +1,48 @@
#version 430 #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 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);
}

View File

@ -14,11 +14,8 @@ unsigned int indices[] = {
1, 2, 3 1, 2, 3
}; };
SDL_Window* sdlWindow;
SDL_GLContext* sdlContext; SDL_GLContext* sdlContext;
GLuint compileShader(const char* path, GLenum type);
SDL_Window* gfxInit(int width, int height) SDL_Window* gfxInit(int width, int height)
{ {
// load sdl modules // 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_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
sdlWindow = SDL_CreateWindow( SDL_Window *sdlWindow = SDL_CreateWindow(
"oglc", "oglc",
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
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); glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &imageUnits);
printf("max texture image units %d\n", imageUnits); printf("max texture image units %d\n", imageUnits);
printWorkGroupLimits();
return sdlWindow; 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 vs = compileShader(vsPath, GL_VERTEX_SHADER);
GLuint fs = compileShader(fsPath, GL_FRAGMENT_SHADER); GLuint fs = compileShader(fsPath, GL_FRAGMENT_SHADER);
unsigned int shaderProgram = glCreateProgram(); GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vs); glAttachShader(shaderProgram, vs);
glAttachShader(shaderProgram, fs); glAttachShader(shaderProgram, fs);
// TODO: check program linking success // TODO: check program linking success textur
glLinkProgram(shaderProgram); glLinkProgram(shaderProgram);
GLint result = GL_FALSE; GLint result = GL_FALSE;
@ -99,7 +113,7 @@ unsigned int compileQuadShaderProgram(const char* vsPath, const char* fsPath)
return shaderProgram; return shaderProgram;
} }
unsigned int compileComputeShaderProgram(const char* computeShaderPath) GLuint compileComputeShaderProgram(const char* computeShaderPath)
{ {
GLuint cs = compileShader(computeShaderPath, GL_COMPUTE_SHADER); GLuint cs = compileShader(computeShaderPath, GL_COMPUTE_SHADER);
GLuint computeProgram = glCreateProgram(); GLuint computeProgram = glCreateProgram();
@ -160,15 +174,40 @@ void setVertexAttributes()
// color // color
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, (void*)(3*sizeof(float))); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, stride, (void*)(3*sizeof(float)));
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
// uv // uv
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, stride, (void*)(6*sizeof(float))); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, stride, (void*)(6*sizeof(float)));
glEnableVertexAttribArray(2); 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 // creates a noise texture in active texture 1
GLuint createNoiseTexture(int width, int height) GLuint createNoiseTexture(int width, int height)
{ {
// same init steps as with a regular texture // same init steps as with a regular texture
GLuint texture; GLuint texture;
glGenTextures(1, &texture); glGenTextures(1, &texture);
@ -216,9 +255,27 @@ GLuint createWriteOnlyTexture(int width, int height)
return texture; 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() void initBuffers()
{ {
unsigned int VAO, VBO, EBO; GLuint VAO, VBO, EBO;
glGenVertexArrays(1, &VAO); // vertex array object glGenVertexArrays(1, &VAO); // vertex array object
glGenBuffers(1, &VBO); // vertex buffer object glGenBuffers(1, &VBO); // vertex buffer object
glGenBuffers(1, &EBO); // element buffer object glGenBuffers(1, &EBO); // element buffer object

View File

@ -9,15 +9,46 @@
#include "io.h" #include "io.h"
#include "random.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); SDL_Window* gfxInit(int width, int height);
unsigned int compileQuadShaderProgram(const char* vsPath, const char* fsPath); int compileShaders(struct Shaders *shaders);
unsigned int compileComputeShaderProgram(); 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 createNoiseTexture(int width, int height);
GLuint createTexture(int width, int height);
GLuint createWriteOnlyTexture(int width, int height); GLuint createWriteOnlyTexture(int width, int height);
GLuint compileShader(const char* path, GLenum type);
void printWorkGroupLimits(); void printWorkGroupLimits();
// quad initialisation
void setVertexAttributes(); void setVertexAttributes();
void initBuffers(); void initBuffers();

View File

@ -1,3 +1,4 @@
#include "main.h" #include "main.h"
#include "gfx.h" #include "gfx.h"
#include "clock.h" #include "clock.h"
@ -5,72 +6,76 @@
#include "sphere.h" #include "sphere.h"
#include "cam.h" #include "cam.h"
#include "input.h"
const int WIDTH = 420; const int WIDTH = 420;
const int HEIGHT = 420; const int HEIGHT = 420;
void updateUniforms(GLuint shaderProgram); void updateUniforms(GLuint shaderProgram);
struct TextureIDs SDL_Window *window;
{
GLuint output; // the texture that ultimately gets rendered
GLuint noise;
} textureIds;
int main() struct Shaders shaders;
struct Textures textures;
void initialise()
{ {
printf("GL_TEXTURE0: %d\n", GL_TEXTURE0); window = gfxInit(WIDTH, HEIGHT);
printf("GL_TEXTURE1: %d\n", GL_TEXTURE1);
randomInit(); 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 // initialise quad
initBuffers(); initBuffers();
setVertexAttributes(); setVertexAttributes();
int frames = 0; // compile shader programs
// render loop compileShaders(&shaders);
while (!checkQuit()) createTextures(WIDTH, HEIGHT, shaders, &textures);
}
int main()
{
initialise();
int frames;
for (frames = 0; !checkQuit(); frames++)
{ {
// dispatch compute shader GLuint shader;
glUseProgram(computeProgram);
updateUniforms(computeProgram); // 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); glDispatchCompute((GLuint)WIDTH, (GLuint)HEIGHT, 1);
// make sure we're finished writing to the texture before trying to read it // make sure we're finished writing to the texture before trying to read it
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
// normal drawing pass // normal drawing pass
glUseProgram(quadProgram); shader = shaders.quad;
glUseProgram(shader);
// bind texture written to by compute stage to 2d target // bind texture written to by compute stage to 2d target
glBindTexture(GL_TEXTURE_2D, textureIds.output);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
// swip swap // swip swap
SDL_GL_SwapWindow(window); SDL_GL_SwapWindow(window);
frames++;
} }
float elapsed = now(); float elapsed = now();