Compare commits

...

5 Commits

Author SHA1 Message Date
ktyl 48e78fedda add multiple samples per pixel 2023-02-24 01:15:25 +00:00
ktyl 7367a2abaf add depth to reflections 2023-02-24 01:15:12 +00:00
ktyl 0f21b16ed7 control central sphere from application code 2023-02-23 00:00:25 +00:00
ktyl b3c5440736 track real and scaled time 2022-06-25 22:09:19 +01:00
ktyl 2fd7797e7d add option to stop time 2022-06-21 00:41:52 +01:00
17 changed files with 131 additions and 74 deletions

7
launch
View File

@ -1,12 +1,13 @@
#!/usr/bin/env bash #!/usr/bin/env bash
executable="$1" executable="$1"
options="$2"
prime="prime-run" prime="prime-run"
if command -v $prime &> /dev/null if command -v $prime &> /dev/null
then then
echo "launching $executable with nvidia prime offloading" echo "launching $executable $options with nvidia prime offloading"
$prime $executable $prime $executable $options
else else
$executable $executable $options
fi fi

View File

@ -46,7 +46,11 @@ clean:
-rm -r $(BIN_DIR) -rm -r $(BIN_DIR)
-rm */*.o -rm */*.o
# TODO: extract run-* out to like, python scripts? arguments can be implemented with argparse to
# manage simple interface with C binary
run: $(TARGET) run: $(TARGET)
$(LAUNCH) $(TARGET) $(LAUNCH) $(TARGET) 1.0
run-converge: $(TARGET)
$(LAUNCH) $(TARGET) 0.0
.PHONY: run clean .PHONY: run clean

View File

@ -1,7 +1,7 @@
const float INF = 50.0; const float INF = 30.0;
const float PI = 3.14159; const float PI = 3.14159;
const float E = 2.71828; const float E = 2.71828;
const int BOUNCES = 4; const int BOUNCES = 5;
// materials // materials
const int MAT_SKY = -1; const int MAT_SKY = -1;

16
shader/include/depth.glsl Normal file
View File

@ -0,0 +1,16 @@
float getLinearDepth(float distance)
{
return distance/INF;
}
float getLogarithmicDepth(float distance)
{
// n roughly correlates to steepness of log curve
// TODO: what does this mean in mathematical terms??
float n = 4;
float f = INF;
float z = distance;
// logarithmic depth
return max(0,log(z*pow(E,n)/f)/n);
}

View File

@ -33,7 +33,7 @@ void intersectPlane(Ray ray, inout RayHit bestHit, vec3 p, vec3 normal)
bestHit.distance = t; bestHit.distance = t;
bestHit.position = ray.origin + t*ray.direction; bestHit.position = ray.origin + t*ray.direction;
bestHit.normal = normal; bestHit.normal = normal;
bestHit.albedo = vec3(1.0,1.0,1.0); bestHit.albedo = vec3(1.0,.4,.4);
bestHit.material = MAT_LAMBERT; bestHit.material = MAT_LAMBERT;
} }
} }

View File

@ -3,6 +3,7 @@ struct Ray
vec3 origin; vec3 origin;
vec3 direction; vec3 direction;
vec3 energy; vec3 energy;
float distance;
}; };
Ray createRay(vec3 origin, vec3 direction) Ray createRay(vec3 origin, vec3 direction)
{ {
@ -11,6 +12,7 @@ Ray createRay(vec3 origin, vec3 direction)
ray.origin = origin; ray.origin = origin;
ray.direction = direction; ray.direction = direction;
ray.energy = vec3(1.0,1.0,1.0); ray.energy = vec3(1.0,1.0,1.0);
ray.distance = 0;
return ray; return ray;
} }

View File

@ -1,19 +1,17 @@
RayHit trace(Ray ray) RayHit trace(inout Ray ray)
{ {
RayHit hit = createRayHit(); RayHit hit = createRayHit();
Sphere s; // floor
s.cr = vec4(0.0,0.0,0.0,5.0);
s.albedo = vec3(1.0,0.0,0.0);
s.material = MAT_CHROME;
intersectSphere(ray, hit, s);
intersectPlane(ray, hit, vec3(0.0,-1.5,0.0),vec3(0.0,1.0,0.0)); intersectPlane(ray, hit, vec3(0.0,-1.5,0.0),vec3(0.0,1.0,0.0));
// spheres
for (int i = 0; i < _activeSpheres; i++) for (int i = 0; i < _activeSpheres; i++)
{ {
intersectSphere(ray, hit, _spheres[i]); intersectSphere(ray, hit, _spheres[i]);
} }
ray.distance += hit.distance * float(hit.distance < INF);
return hit; return hit;
} }

View File

@ -24,6 +24,7 @@ uniform vec3 _skyColor = vec3(0.68,0.85,0.9);
// scene.glsl includes scene trace function // scene.glsl includes scene trace function
#include scene.glsl #include scene.glsl
#include lighting.glsl #include lighting.glsl
#include depth.glsl
vec3 shade(inout Ray ray, RayHit hit) vec3 shade(inout Ray ray, RayHit hit)
{ {
@ -77,14 +78,25 @@ void main()
int bounces = (1-sky) * BOUNCES; int bounces = (1-sky) * BOUNCES;
pixel.xyz = mix(pixel.xyz, _skyColor, sky); pixel.xyz = mix(pixel.xyz, _skyColor, sky);
// trace the rays path around the scene // sample
for (int j = 0; j < bounces; j++) int samples = 2;
for (int i = 0; i < samples; i++)
{ {
RayHit hit = trace(ray); float sampleDepth = 0;
pixel.xyz += ray.energy * shade(ray, hit); // trace the ray's path around the scene
for (int j = 0; j < bounces; j++)
{
RayHit hit = trace(ray);
if (length(ray.energy) < 0.001) break; depth = getLogarithmicDepth(ray.distance);
pixel.xyz += ray.energy * shade(ray, hit);
if (length(ray.energy) < 0.001) break;
}
depth += sampleDepth / float(samples);
} }
pixel.xyz = mix(pixel.xyz, vec3(1.0), depth); pixel.xyz = mix(pixel.xyz, vec3(1.0), depth);

View File

@ -18,6 +18,7 @@ layout (rgba32f, binding = 3) writeonly uniform image2D g1_output;
#include camera.glsl #include camera.glsl
#include image.glsl #include image.glsl
#include scene.glsl #include scene.glsl
#include depth.glsl
void main() void main()
{ {
@ -34,18 +35,7 @@ void main()
Ray ray = createCameraRay(uv); Ray ray = createCameraRay(uv);
RayHit hit = trace(ray); RayHit hit = trace(ray);
// n roughly correlates to steepness of log curve float depth = getLogarithmicDepth(hit.distance);
// TODO: what does this mean in mathematical terms??
float n = 4;
float f = INF;
float z = hit.distance;
float depth;
// linear depth
//depth = z/f;
// logarithmic depth
depth = max(0,log(z*pow(E,n)/f)/n);
// pack normal into texture // pack normal into texture
pixel.xyz = hit.normal*0.5+0.5; pixel.xyz = hit.normal*0.5+0.5;

View File

@ -2,10 +2,8 @@
const float FOVY = 90.0; const float FOVY = 90.0;
void updateCameraUniforms(GLuint shaderProgram, float aspect) void updateCameraUniforms(GLuint shaderProgram, float aspect, float t)
{ {
float t = now();
// wobble up dir // wobble up dir
vec3 cdir, cright, cup; vec3 cdir, cright, cup;
vec3 up = {0.1*sin(t),1.0,0.05*cos(0.5*t)}; vec3 up = {0.1*sin(t),1.0,0.05*cos(0.5*t)};

View File

@ -6,4 +6,4 @@
#include "clock.h" #include "clock.h"
void updateCameraUniforms(GLuint shaderProgram, float aspect); void updateCameraUniforms(GLuint shaderProgram, float aspect, float t);

View File

@ -4,3 +4,8 @@ float now()
{ {
return (float)SDL_GetTicks() / 1000.0; return (float)SDL_GetTicks() / 1000.0;
} }
float nowScaled(struct Epoch t)
{
return t.scale * now();
}

View File

@ -2,5 +2,12 @@
#include <SDL2/SDL.h> #include <SDL2/SDL.h>
// seconds since program start struct Epoch
{
float scale;
};
// real seconds since program start
float now(); float now();
// simulation seconds since program start
float nowScaled(struct Epoch e);

View File

@ -174,16 +174,6 @@ 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);

View File

@ -10,12 +10,13 @@
const int WIDTH = 420; const int WIDTH = 420;
const int HEIGHT = 420; const int HEIGHT = 420;
void updateUniforms(GLuint shaderProgram); void updateUniforms(GLuint shaderProgram, float t);
SDL_Window *window; SDL_Window *window;
struct Shaders shaders; struct Shaders shaders;
struct Textures textures; struct Textures textures;
struct Epoch epoch;
void initialise() void initialise()
{ {
@ -32,8 +33,21 @@ void initialise()
createTextures(WIDTH, HEIGHT, shaders, &textures); createTextures(WIDTH, HEIGHT, shaders, &textures);
} }
int main() void parseArgs(int argc, char* argv[], struct Epoch* e)
{ {
// check we have the right number of arguments
if (argc != 2)
{
fprintf(stderr, "usage: oglc TIMESPEED\n");
}
sscanf(argv[1], "%f", &(e->scale));
}
int main(int argc, char* argv[])
{
parseArgs(argc, argv, &epoch);
initialise(); initialise();
float start = now(); float start = now();
@ -41,6 +55,7 @@ int main()
for (frames = 0; !checkQuit(); frames++) for (frames = 0; !checkQuit(); frames++)
{ {
GLuint shader; GLuint shader;
float t = nowScaled(epoch);
// prepass // prepass
// TODO: write output to different texture than main output // TODO: write output to different texture than main output
@ -48,18 +63,17 @@ int main()
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures.g0); glBindTexture(GL_TEXTURE_2D, textures.g0);
glUseProgram(shader); glUseProgram(shader);
updateUniforms(shader); updateUniforms(shader, t);
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_ALL_BARRIER_BITS); glMemoryBarrier(GL_ALL_BARRIER_BITS);
// dispatch compute shaders
shader = shaders.lighting; shader = shaders.lighting;
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, textures.target); glBindTexture(GL_TEXTURE_2D, textures.target);
glUseProgram(shader); glUseProgram(shader);
updateUniforms(shader); updateUniforms(shader, t);
//int loc = glGetUniformLocation(shader, "_g0"); //int loc = glGetUniformLocation(shader, "_g0");
//glUniform1i(loc, textures.g0); //glUniform1i(loc, textures.g0);
glDispatchCompute((GLuint)WIDTH, (GLuint)HEIGHT, 1); glDispatchCompute((GLuint)WIDTH, (GLuint)HEIGHT, 1);
@ -67,6 +81,18 @@ int main()
// 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);
// TODO: frame blending
//
// before we get to our normal rendering pass we need to blend our samples
// across multiple frames. to do this, we should maintain an accumulation
// buffer where we can store pixel data while a new frame is being generated.
// this can be a collection of two buffers, where one is written to and the
// other is read from. to render a new frame, the newly sampled raw frame can
// be combined with data from the previous frame. by repeatedly taking the
// average over a large number of frames a cleaner image can be generated.
// this is most effective for static images, and will produce motion blur on
// a moving image.
// normal drawing pass // normal drawing pass
shader = shaders.quad; shader = shaders.quad;
glUseProgram(shader); glUseProgram(shader);
@ -87,7 +113,7 @@ int main()
return 0; return 0;
} }
void updateUniforms(GLuint shaderProgram) void updateUniforms(GLuint shaderProgram, float t)
{ {
// update random values // update random values
vec4 seed = vec4 seed =
@ -100,19 +126,17 @@ void updateUniforms(GLuint shaderProgram)
int loc = glGetUniformLocation(shaderProgram, "_seed"); int loc = glGetUniformLocation(shaderProgram, "_seed");
glUniform4fv(loc, 1, seed); glUniform4fv(loc, 1, seed);
// update time
float t = now();
float sin_t = sin(t); float sin_t = sin(t);
loc = glGetUniformLocation(shaderProgram, "_t"); loc = glGetUniformLocation(shaderProgram, "_t");
glUniform4f(loc, t, sin_t, (1.0 + sin_t)*0.5, 0.0f); glUniform4f(loc, t, sin_t, (1.0 + sin_t)*0.5, 0.0f);
// update camera // update camera
float aspect = (float)WIDTH/(float)HEIGHT; float aspect = (float)WIDTH/(float)HEIGHT;
updateCameraUniforms(shaderProgram, aspect); updateCameraUniforms(shaderProgram, aspect, t);
// make and update spheres // make and update spheres
const int sphereCount = 42; const int sphereCount = 41;
struct Sphere spheres[sphereCount]; struct Sphere spheres[sphereCount];
makeSpheres(spheres, sphereCount); makeSpheres(spheres, sphereCount, t);
updateSphereUniforms(shaderProgram, spheres, sphereCount); updateSphereUniforms(shaderProgram, spheres, sphereCount);
} }

View File

@ -1,9 +1,7 @@
#include "sphere.h" #include "sphere.h"
void makeSpheres(struct Sphere *spheres, int count) void makeSpheres(struct Sphere *spheres, int count, float t)
{ {
//float t = time();
vec3 albedos[] = vec3 albedos[] =
{ {
{0.0,0.0,1.0}, {0.0,0.0,1.0},
@ -15,21 +13,33 @@ void makeSpheres(struct Sphere *spheres, int count)
{1.0,1.0,1.0} {1.0,1.0,1.0}
}; };
// distance from center
float d = 6.0;
float radius = 0.5;
float x;
vec3 sc = {0.0,0.0,1.0}; vec3 sc = {0.0,0.0,1.0};
float t = now(); int sphereIdx = 0;
for (int i = 0; i < count; i++)
// a shiny sphere in the middle which bobs around
int middleSpheres = 1;
sc[0] = 2.5*sin(t*2.67);
sc[1] = 1.5+sin(t*1.09);
sc[2] = 2.5*cos(-t*2.13);
float radius = 1.7;
vec3 col = {0.0,0.0,0.0};
spheres[sphereIdx++] = makeSphere(sc,radius,col,1);
// rainbow spheres
int rainbowSpheres = count - middleSpheres;
// distance from center
float d = 6.0;
radius = 0.5;
float x;
for (int i = 0; i < rainbowSpheres; i++)
{ {
x = 2.0*CGLM_PI * (float)i/(float)count; x = 2.0*CGLM_PI * (float)i/(float)rainbowSpheres;
sc[0] = sin(x)*d; sc[0] = sin(x)*d;
sc[1] = sin(x*3.0-5.0*sin(t)); sc[1] = sin(x*3.0-5.0*sin(t));
sc[2] = cos(x)*d; sc[2] = cos(x)*d;
float ic = i/(float)count*CGLM_PI*2.0; float ic = i/(float)rainbowSpheres*CGLM_PI*2.0;
float r = sin(ic); float r = sin(ic);
float g = sin(ic+CGLM_PI/1.5); float g = sin(ic+CGLM_PI/1.5);
float b = sin(ic+2.0*CGLM_PI/1.5); float b = sin(ic+2.0*CGLM_PI/1.5);
@ -38,8 +48,8 @@ void makeSpheres(struct Sphere *spheres, int count)
glm_vec3_scale(col, 0.5, col); glm_vec3_scale(col, 0.5, col);
glm_vec3_adds(col, 0.5, col); glm_vec3_adds(col, 0.5, col);
int material = i % 2 == 0; int material = i % 2;
spheres[i] = makeSphere(sc,radius,col,material); spheres[sphereIdx++] = makeSphere(sc,radius,col,material);
} }
} }

View File

@ -16,7 +16,7 @@ struct Sphere
int material; int material;
}; };
void makeSpheres(struct Sphere *spheres, int count); void makeSpheres(struct Sphere *spheres, int count, float t);
struct Sphere makeSphere(vec3 center, float radius, vec3 albedo, int material); struct Sphere makeSphere(vec3 center, float radius, vec3 albedo, int material);
//void updateSphereUniform(GLuint shaderProgram, struct Sphere sphere); //void updateSphereUniform(GLuint shaderProgram, struct Sphere sphere);
void updateSphereUniforms(GLuint shaderProgram, struct Sphere *spheres, int count); void updateSphereUniforms(GLuint shaderProgram, struct Sphere *spheres, int count);