diff --git a/README.md b/README.md index 86a921b..ddad3a0 100644 --- a/README.md +++ b/README.md @@ -1 +1,20 @@ -# processing \ No newline at end of file +# Path tracer + +## Render Gallery + +**All render frames converged within 0.5s on a gtx 1080**

+**NOTE: if the fps is abysmal on your machine, you may need to lower the SPF located in the fragment shader**

+![image](https://media.discordapp.net/attachments/640631259730542602/780539227221000243/unknown.png) +![image](https://media.discordapp.net/attachments/764510825623977997/780537058719957012/unknown.png) +![image](https://media.discordapp.net/attachments/640631259730542602/780581710332952576/unknown.png) +![image](https://media.discordapp.net/attachments/640631259730542602/780585666257616907/unknown.png) +![image](https://media.discordapp.net/attachments/764510825623977997/778461138601902110/unknown.png) +![image](https://media.discordapp.net/attachments/640631259730542602/780551399276412929/unknown.png) +![image](https://media.discordapp.net/attachments/764510825623977997/778461107919519764/unknown.png) +## Some earlier renders (images are very noisy) +![image](https://media.discordapp.net/attachments/640631259730542602/777073655359602708/unknown.png) +![image](https://media.discordapp.net/attachments/640631259730542602/777076703276171264/unknown.png) +![image](https://media.discordapp.net/attachments/640631259730542602/777071652403544084/unknown.png) +![image](https://user-images.githubusercontent.com/19439575/99139449-8c85fc00-25ed-11eb-940a-6d6c764ce353.png) +![image](https://user-images.githubusercontent.com/19439575/99134277-d95bd980-25d1-11eb-9052-380018af8ec3.png) +![image](https://user-images.githubusercontent.com/19439575/99129174-b6c1c480-25c1-11eb-9c88-1920dd577207.png) \ No newline at end of file diff --git a/Runner/Buffer.pde b/Runner/Buffer.pde new file mode 100644 index 0000000..ac6f84e --- /dev/null +++ b/Runner/Buffer.pde @@ -0,0 +1,38 @@ +import java.nio.*; +import com.jogamp.opengl.GL4; + +public class Buffer { + int id; + + Buffer() {} + + void create() { + int ids[] = new int[1]; + gl.glCreateBuffers(1, ids, 0); + id = ids[0]; + } + + void delete() { + int ids[] = new int[1]; + ids[0] = id; + gl.glDeleteBuffers(1, ids, 0); + } + + void init(long size, java.nio.Buffer data, int target, int usage) { + bind(target); + gl.glBufferData(target, size, data, usage); + unbind(target); + } + + void update(long offset, long size, java.nio.Buffer data) { // not supported by PJOGL + gl.glNamedBufferSubData(id, offset, size, data); + } + + void bind(int target) { + gl.glBindBuffer(target, id); + } + + void unbind(int target){ + gl.glBindBuffer(target, 0); + } +} diff --git a/Runner/Camera.pde b/Runner/Camera.pde new file mode 100644 index 0000000..c34eb12 --- /dev/null +++ b/Runner/Camera.pde @@ -0,0 +1,105 @@ +public class Camera { + float yaw = 0; + float pitch = 0; + float prevX = screenSize[0]/2; + float prevY = screenSize[1]/2; + + vec3 pos; + vec3 dir; + vec3 up; + + mat4 view, invView; + mat4 proj, invProj; + final float rotSpeed = 0.05f; + final float moveSpeed = 0.010f; + + int shaderProjLoc, shaderViewLoc, invProjLoc, invViewLoc; + boolean wKey = false, sKey = false, aKey = false, dKey = false, spaceKey = false, shiftKey = false, isActive = true; + + public Camera(float posx, float posy, float posz, float dirx, float diry, float dirz) { + proj = MMath.perspective(MMath.radians(45.0f), (float)screenSize[0] / (float)screenSize[1], 0.1, 2000.0f); + invProj = MMath.inverse(proj); + pos = new vec3(posx, posy, posz); + dir = new vec3(dirx, diry, dirz); + up = new vec3(0, 1, 0); + view = MMath.lookAt(pos, dir, up); + win = (GLWindow)surface.getNative(); + } + + public void mouseCallback() { + if(!isActive) return; + + float dx = prevX-mouseX; + float dy = prevY-mouseY; + prevX = mouseX; + prevY = mouseY; + yaw += dx * rotSpeed; + pitch += dy * rotSpeed; + if(pitch > 89.0) + pitch = 89.0; + if (pitch < -89.0) + pitch = -89.0; + + dir = MMath.normalize(new vec3(sin(MMath.radians(yaw)) * cos(MMath.radians(pitch)), + sin(MMath.radians(pitch)), + cos(MMath.radians(yaw)) * cos(MMath.radians(pitch)))); + + prevX = screenSize[0]/2.0; // recenter mouse + prevY = screenSize[1]/2.0; + win.warpPointer((int) prevX, (int) prevY); + } + + public boolean setKbState(final int _keyCode, final char _key, final boolean state) { + if(!isActive) return state; + switch(_key) { + case 'w': + case 'W': + return wKey = state; // forward + case 's': + case 'S': + return sKey = state; // backward + case 'a': + case 'A': + return aKey = state; // left + case 'd': + case 'D': + return dKey = state; // right + case ' ': + return spaceKey = state; // up + default: + switch(_keyCode) { + case SHIFT: + return shiftKey = state; + default: + return state; + } + } + } + + public void updatePos(float dt) { + if(!isActive) return; + if (wKey) + pos.add(MMath.mul(dir, moveSpeed * dt)); + if (sKey) + pos.add(MMath.mul(dir, moveSpeed * dt * -1)); + if (aKey) + pos.add(MMath.mul(MMath.normalize(MMath.cross(up, dir)), moveSpeed * dt)); + if (dKey) + pos.add(MMath.mul(MMath.normalize(MMath.cross(up, dir)), moveSpeed * dt * -1)); + if (spaceKey) + pos.add(MMath.mul(MMath.normalize(up), moveSpeed * dt)); + if (shiftKey) + pos.add(MMath.mul(MMath.normalize(up), moveSpeed * dt * -1)); + } + + void updateUniforms() { + mat4 currView = MMath.lookAt(pos, MMath.add(pos, dir), up); + if(!view.equals(currView)) { // camera view or position changed + screenSize[2] = 0; + view.m = currView.m; + invView = MMath.inverse(view); + gl.glUniformMatrix4fv(shaderViewLoc, 1, false, view.m, 0); + gl.glUniformMatrix4fv(invViewLoc, 1, false, invView.m, 0); + } + } +} diff --git a/Runner/Math.pde b/Runner/Math.pde new file mode 100644 index 0000000..aa7e245 --- /dev/null +++ b/Runner/Math.pde @@ -0,0 +1,180 @@ +public static class vec3 { + public float x; + public float y; + public float z; + + public vec3(float _x, float _y, float _z) { + x = _x; + y = _y; + z = _z; + } + + public void add(vec3 other) { + x += other.x; + y += other.y; + z += other.z; + } + + public String toString() { + return "vec3(" + x + ", " + y + ", " + z + ")"; + } +} + +public static class mat4 { + // row-major + public float m[]; + + public mat4() { + //defaults to identity matrix + m = new float[16]; + set(0,0,1); + set(1,1,1); + set(2,2,1); + set(3,3,1); + } + + public void zero() { + for(int i = 0; i < 16; ++i) { + m[i] = 0; + } + } + + public void set(int row, int col, float val) { + m[row*4+col] = val; + } + + public float get(int row, int col) { + return m[row*4+col]; + } + + public boolean equals(mat4 other) { + for(int i = 0; i < 16; ++i) { + if(m[i] != other.m[i]) return false; + } + return true; + } +} + +public static class MMath { + public static final float PI = 3.141592; + private MMath() {} + + public static vec3 normalize(vec3 vec) { + float len = sqrt(vec.x*vec.x + vec.y*vec.y + vec.z*vec.z); + return new vec3(vec.x/len, vec.y/len, vec.z/len); + } + + public static vec3 add(vec3 a, vec3 b) { // no operator overloading in java :( + return new vec3(a.x+b.x, a.y+b.y, a.z+b.z); + } + + public static vec3 sub(vec3 a, vec3 b) { + return new vec3(a.x-b.x, a.y-b.y, a.z-b.z); + } + + public static vec3 mul(vec3 a, float scalar) { + return new vec3(a.x*scalar, a.y*scalar, a.z*scalar); + } + + public static float dot(vec3 a, vec3 b) { + return a.x*b.x + a.y*b.y + a.z*b.z; + } + + public static vec3 cross(vec3 a, vec3 b) { + float x = a.y*b.z - a.z*b.y; + float y = a.z*b.x - a.x*b.z; + float z = a.x*b.y - a.y*b.x; + return new vec3(x, y, z); + } + + public static float radians(float deg) { + return deg * MMath.PI / 180.0f; + } + + public static mat4 perspective(float fov, float ratio, float near, float far) { + // impl identical to glm's perspective + // credit: https://github.com/g-truc/glm/blob/0.9.5/glm/gtc/matrix_transform.inl#L208 + float halfTanFov = tan(fov/2); + mat4 result = new mat4(); + result.zero(); + result.set(0,0,1 / (ratio * halfTanFov)); + result.set(1,1,1 / halfTanFov); + result.set(2,2,-(far + near) / (far - near)); + result.set(2,3,-1); + result.set(3,2,-(2 * far * near) / (far - near)); + return result; + } + + public static mat4 lookAt(vec3 pos, vec3 center, vec3 up) { + // impl identical to glm's lookAt + // credit: https://github.com/g-truc/glm/blob/0.9.5/glm/gtc/matrix_transform.inl#L385 + vec3 f = normalize(sub(center, pos)); + vec3 s = normalize(cross(f, up)); + vec3 u = cross(s, f); + mat4 result = new mat4(); + + result.set(0,0,s.x); + result.set(1,0,s.y); + result.set(2,0,s.z); + result.set(0,1,u.x); + result.set(1,1,u.y); + result.set(2,1,u.z); + result.set(0,2, -f.x); + result.set(1,2, -f.y); + result.set(2,2, -f.z); + result.set(3,0, -dot(s, pos)); + result.set(3,1, -dot(u, pos)); + result.set(3,2,dot(f, pos)); + return result; + } + + public static mat4 inverse(mat4 mat) { + // credit to David Moore, https://cgit.freedesktop.org/mesa/glu/tree/src/libutil/project.c + float det; + mat4 result = new mat4(); + + result.m[0] = mat.m[5]*mat.m[10]*mat.m[15] - mat.m[5]*mat.m[11]*mat.m[14] - mat.m[9]*mat.m[6]*mat.m[15] + + mat.m[9]*mat.m[7]*mat.m[14] + mat.m[13]*mat.m[6]*mat.m[11] - mat.m[13]*mat.m[7]*mat.m[10]; + result.m[4] = -mat.m[4]*mat.m[10]*mat.m[15] + mat.m[4]*mat.m[11]*mat.m[14] + mat.m[8]*mat.m[6]*mat.m[15] + - mat.m[8]*mat.m[7]*mat.m[14] - mat.m[12]*mat.m[6]*mat.m[11] + mat.m[12]*mat.m[7]*mat.m[10]; + result.m[8] = mat.m[4]*mat.m[9]*mat.m[15] - mat.m[4]*mat.m[11]*mat.m[13] - mat.m[8]*mat.m[5]*mat.m[15] + + mat.m[8]*mat.m[7]*mat.m[13] + mat.m[12]*mat.m[5]*mat.m[11] - mat.m[12]*mat.m[7]*mat.m[9]; + result.m[12] = -mat.m[4]*mat.m[9]*mat.m[14] + mat.m[4]*mat.m[10]*mat.m[13] + mat.m[8]*mat.m[5]*mat.m[14] + - mat.m[8]*mat.m[6]*mat.m[13] - mat.m[12]*mat.m[5]*mat.m[10] + mat.m[12]*mat.m[6]*mat.m[9]; + result.m[1] = -mat.m[1]*mat.m[10]*mat.m[15] + mat.m[1]*mat.m[11]*mat.m[14] + mat.m[9]*mat.m[2]*mat.m[15] + - mat.m[9]*mat.m[3]*mat.m[14] - mat.m[13]*mat.m[2]*mat.m[11] + mat.m[13]*mat.m[3]*mat.m[10]; + result.m[5] = mat.m[0]*mat.m[10]*mat.m[15] - mat.m[0]*mat.m[11]*mat.m[14] - mat.m[8]*mat.m[2]*mat.m[15] + + mat.m[8]*mat.m[3]*mat.m[14] + mat.m[12]*mat.m[2]*mat.m[11] - mat.m[12]*mat.m[3]*mat.m[10]; + result.m[9] = -mat.m[0]*mat.m[9]*mat.m[15] + mat.m[0]*mat.m[11]*mat.m[13] + mat.m[8]*mat.m[1]*mat.m[15] + - mat.m[8]*mat.m[3]*mat.m[13] - mat.m[12]*mat.m[1]*mat.m[11] + mat.m[12]*mat.m[3]*mat.m[9]; + result.m[13] = mat.m[0]*mat.m[9]*mat.m[14] - mat.m[0]*mat.m[10]*mat.m[13] - mat.m[8]*mat.m[1]*mat.m[14] + + mat.m[8]*mat.m[2]*mat.m[13] + mat.m[12]*mat.m[1]*mat.m[10] - mat.m[12]*mat.m[2]*mat.m[9]; + result.m[2] = mat.m[1]*mat.m[6]*mat.m[15] - mat.m[1]*mat.m[7]*mat.m[14] - mat.m[5]*mat.m[2]*mat.m[15] + + mat.m[5]*mat.m[3]*mat.m[14] + mat.m[13]*mat.m[2]*mat.m[7] - mat.m[13]*mat.m[3]*mat.m[6]; + result.m[6] = -mat.m[0]*mat.m[6]*mat.m[15] + mat.m[0]*mat.m[7]*mat.m[14] + mat.m[4]*mat.m[2]*mat.m[15] + - mat.m[4]*mat.m[3]*mat.m[14] - mat.m[12]*mat.m[2]*mat.m[7] + mat.m[12]*mat.m[3]*mat.m[6]; + result.m[10] = mat.m[0]*mat.m[5]*mat.m[15] - mat.m[0]*mat.m[7]*mat.m[13] - mat.m[4]*mat.m[1]*mat.m[15] + + mat.m[4]*mat.m[3]*mat.m[13] + mat.m[12]*mat.m[1]*mat.m[7] - mat.m[12]*mat.m[3]*mat.m[5]; + result.m[14] = -mat.m[0]*mat.m[5]*mat.m[14] + mat.m[0]*mat.m[6]*mat.m[13] + mat.m[4]*mat.m[1]*mat.m[14] + - mat.m[4]*mat.m[2]*mat.m[13] - mat.m[12]*mat.m[1]*mat.m[6] + mat.m[12]*mat.m[2]*mat.m[5]; + result.m[3] = -mat.m[1]*mat.m[6]*mat.m[11] + mat.m[1]*mat.m[7]*mat.m[10] + mat.m[5]*mat.m[2]*mat.m[11] + - mat.m[5]*mat.m[3]*mat.m[10] - mat.m[9]*mat.m[2]*mat.m[7] + mat.m[9]*mat.m[3]*mat.m[6]; + result.m[7] = mat.m[0]*mat.m[6]*mat.m[11] - mat.m[0]*mat.m[7]*mat.m[10] - mat.m[4]*mat.m[2]*mat.m[11] + + mat.m[4]*mat.m[3]*mat.m[10] + mat.m[8]*mat.m[2]*mat.m[7] - mat.m[8]*mat.m[3]*mat.m[6]; + result.m[11] = -mat.m[0]*mat.m[5]*mat.m[11] + mat.m[0]*mat.m[7]*mat.m[9] + mat.m[4]*mat.m[1]*mat.m[11] + - mat.m[4]*mat.m[3]*mat.m[9] - mat.m[8]*mat.m[1]*mat.m[7] + mat.m[8]*mat.m[3]*mat.m[5]; + result.m[15] = mat.m[0]*mat.m[5]*mat.m[10] - mat.m[0]*mat.m[6]*mat.m[9] - mat.m[4]*mat.m[1]*mat.m[10] + + mat.m[4]*mat.m[2]*mat.m[9] + mat.m[8]*mat.m[1]*mat.m[6] - mat.m[8]*mat.m[2]*mat.m[5]; + + det = mat.m[0]*result.m[0] + mat.m[1]*result.m[4] + mat.m[2]*result.m[8] + mat.m[3]*result.m[12]; + det = 1.0 / det; + + if(det == 0) // no inv exists, just crash + throw new ArithmeticException("Inverse doesn't exist"); + + for (int i = 0; i < 16; i++) + result.m[i] *= det; + + return result; + } +} diff --git a/Runner/Object.pde b/Runner/Object.pde deleted file mode 100644 index fe8bd18..0000000 --- a/Runner/Object.pde +++ /dev/null @@ -1,35 +0,0 @@ -public class MRect -{ - private int w; // single bar width - private float xpos; // rect xposition - private float h; // rect height - private float ypos ; // rect yposition - private float d; // single bar distance - private float t; // number of bars - - public MRect(int iw, float ixp, float ih, float iyp, float id, float it) { - w = iw; - xpos = ixp; - h = ih; - ypos = iyp; - d = id; - t = it; - } - - public void move (float posX, float posY, float damping) { - float dif = ypos - posY; - if (abs(dif) > 1) { - ypos -= dif/damping; - } - dif = xpos - posX; - if (abs(dif) > 1) { - xpos -= dif/damping; - } - } - - public void display() { - for (int i=0; i list = new ArrayList(); + list.add(5); + list.add(10); + for(int a : list) { + System.out.println(a); + } + + frameRate(120); // cap to 60fps + + shader = loadShader("frag.glsl", "vert.glsl"); + quadBuffer = allocateDirectFloatBuffer(quadVerts.length); + quadBuffer.rewind(); + quadBuffer.put(quadVerts); + quadBuffer.rewind(); + win = (GLWindow)surface.getNative(); + win.confinePointer(true); + win.setPointerVisible(false); + win.warpPointer((int)screenSize[0]/2, (int)screenSize[1]/2); + win.setResizable(false); + + cam = new Camera(0, 0, -4, 0, 0, 1); + cam.prevX = mouseX; + cam.prevY = mouseY; + + pgl = (PJOGL)beginPGL(); + gl = pgl.gl.getGL4(); + + vao.create(); + vbo.create(); + vao.addAttrib(0, 0, 3); + vao.addBuffer(vbo, BufferType.VERTEX, Float.BYTES*3); + vbo.init(quadVerts.length*Float.BYTES, quadBuffer, GL.GL_ARRAY_BUFFER, GL.GL_DYNAMIC_DRAW); + shader.bind(); + + screenSizeLoc = gl.glGetUniformLocation(shader.glProgram, "screen_size"); + cam.shaderProjLoc = gl.glGetUniformLocation(shader.glProgram, "cam_proj"); + cam.shaderViewLoc = gl.glGetUniformLocation(shader.glProgram, "cam_view"); + cam.invProjLoc = gl.glGetUniformLocation(shader.glProgram, "inv_cam_proj"); + cam.invViewLoc = gl.glGetUniformLocation(shader.glProgram, "inv_cam_view"); + int outImgLoc = gl.glGetUniformLocation(shader.glProgram, "out_img"); + gl.glUniform3fv(screenSizeLoc, 1, screenSize, 0); + gl.glUniformMatrix4fv(cam.shaderProjLoc, 1, false, cam.proj.m, 0); + gl.glUniformMatrix4fv(cam.invProjLoc, 1, false, cam.invProj.m, 0); + + int texs[]= new int[1]; + gl.glCreateTextures(GL.GL_TEXTURE_2D, 1, texs, 0); + tex = texs[0]; + gl.glTextureStorage2D(tex, 1, GL.GL_RGBA32F, (int)screenSize[0], (int)screenSize[1]); + gl.glUniform1i(outImgLoc, 0); + + endPGL(); +} + +long prev = 0; + +void draw() { + cam.mouseCallback(); + double dt = (-prev + (prev = frameRateLastNanos))/1e6d; + cam.updatePos((float) dt); + cam.updateUniforms(); + gl.glUniform3fv(screenSizeLoc, 1, screenSize, 0); + background(0); + shader.bind(); + gl.glBindTextureUnit(0, tex); + gl.glBindImageTexture(0, tex, 0, false, 0, GL4.GL_READ_WRITE, GL.GL_RGBA32F); + vao.bind(); + vbo.bind(GL.GL_ARRAY_BUFFER); + gl.glDrawArrays(GL.GL_TRIANGLES, 0, quadVerts.length); + surface.setTitle("FPS: " + frameRate); + if(screenSize[2] < 1e6) ++screenSize[2]; } diff --git a/Runner/VertexArray.pde b/Runner/VertexArray.pde new file mode 100644 index 0000000..8a29f02 --- /dev/null +++ b/Runner/VertexArray.pde @@ -0,0 +1,35 @@ +import com.jogamp.opengl.GL4; + +public enum BufferType { + VERTEX, + INDEX, +} + +class VertexArray { + int id; + + void create() { + int ids[] = new int[1]; + ids[0] = id; + gl.glCreateVertexArrays(1, ids, 0); + } + + void addBuffer(Buffer buffer, BufferType type, int stride) { + switch(type) { + case VERTEX: + gl.glVertexArrayVertexBuffer(id, 0, buffer.id, 0, stride); + case INDEX: + gl.glVertexArrayElementBuffer(id, buffer.id); + } + } + + void addAttrib(int index, int offset, int count) { + gl.glEnableVertexArrayAttrib(id, index); + gl.glVertexArrayAttribFormat(id, index, count, GL.GL_FLOAT, false, offset); + gl.glVertexArrayAttribBinding(id, index, 0); + } + + void bind(){ + gl.glBindVertexArray(id); + } +} diff --git a/Runner/code/sketch.properties b/Runner/code/sketch.properties new file mode 100644 index 0000000..5d0c318 --- /dev/null +++ b/Runner/code/sketch.properties @@ -0,0 +1,2 @@ +mode=Java +mode.id=processing.mode.java.JavaMode diff --git a/Runner/frag.glsl b/Runner/frag.glsl new file mode 100644 index 0000000..0f4f43f --- /dev/null +++ b/Runner/frag.glsl @@ -0,0 +1,581 @@ +#version 420 core +//#extension GL_EXT_control_flow_attributes : require +#extension GL_EXT_shader_image_load_store : require + +out vec4 frag_color; + +uniform vec3 screen_size; // z is internal counter +uniform mat4 inv_cam_proj; +uniform mat4 inv_cam_view; +layout(rgba32f) uniform image2D out_img; + +#define PI 3.1415926 +#define EPS 1e-3 +#define T_MAX 1e6 + +struct Material { + vec3 albedo; + vec3 specular; + float spec_chance; + float roughness; + float emmitence; + float ior; + uint effect; +}; + +struct Ray { + vec3 origin; + vec3 dir; +}; + +struct Sphere { + vec3 pos; + float r; + uint mat; +}; + +vec3 LessThan(vec3 f, float value) +{ + return vec3( + (f.x < value) ? 1.0f : 0.0f, + (f.y < value) ? 1.0f : 0.0f, + (f.z < value) ? 1.0f : 0.0f); +} + +vec3 LinearToSRGB(vec3 rgb) +{ + rgb = clamp(rgb, 0.0f, 1.0f); + + return mix( + pow(rgb, vec3(1.0f / 2.4f)) * 1.055f - 0.055f, + rgb * 12.92f, + LessThan(rgb, 0.0031308f) + ); +} + +vec3 SRGBToLinear(vec3 rgb) +{ + rgb = clamp(rgb, 0.0f, 1.0f); + + return mix( + pow(((rgb + 0.055f) / 1.055f), vec3(2.4f)), + rgb / 12.92f, + LessThan(rgb, 0.04045f) + ); +} + +uint wang_hash(inout uint seed) { + seed = uint(seed ^ uint(61)) ^ uint(seed >> uint(16)); + seed *= uint(9); + seed = seed ^ (seed >> 4); + seed *= uint(0x27d4eb2d); + seed = seed ^ (seed >> 15); + return seed; +} + +// ACES tone mapping curve fit to go from HDR to LDR +//https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ +vec3 ACESFilm(vec3 x) +{ + float a = 2.51f; + float b = 0.03f; + float c = 2.43f; + float d = 0.59f; + float e = 0.14f; + return clamp((x*(a*x + b)) / (x*(c*x + d) + e), 0.0f, 1.0f); +} + +float rand3D(in vec3 co) { + return fract(sin(dot(co.xyz ,vec3(12.9898,78.233,144.7272))) * 43758.5453); +} + +// Generates a seed for a random number generator from 2 inputs plus a backoff +// https://github.com/nvpro-samples/optix_prime_baking/blob/332a886f1ac46c0b3eea9e89a59593470c755a0e/random.h +// https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_jitter_cam +// https://en.wikipedia.org/wiki/Tiny_Encryption_Algorithm +uint init_rand_seed(uint val0, uint val1) { + uint v0 = val0, v1 = val1, s0 = 0; + //[[unroll]] hopefully unrolls automatically + for (uint n = 0; n < 16; n++){ + s0 += 0x9e3779b9; + v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4); + v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e); + } + + return v0; +} + +float rand(inout uint state) { + return float(wang_hash(state)) / 4294967296.0; +} + +vec3 rand_vec(inout uint state) { + float z = rand(state) * 2.0f - 1.0f; + float a = rand(state) * 2 * PI; + float r = sqrt(1.0f - z * z); + float x = r * cos(a); + float y = r * sin(a); + return vec3(x, y, z); +} + +// schlick's approximation of the fresnel effect +float fresnel_coe(float n1, float n2, vec3 normal, vec3 incident, float f0, float f90){ + float r0 = (n1-n2) / (n1+n2); + r0 *= r0; + float cosX = -dot(normal, incident); + if (n1 > n2) { + float n = n1/n2; + float sinT2 = n*n*(1.0-cosX*cosX); + // Total internal reflection + if (sinT2 > 1.0) + return f90; + cosX = sqrt(1.0-sinT2); + } + float x = 1.0-cosX; + float ret = r0+(1.0-r0)*x*x*x*x*x; + + return mix(f0, f90, ret); +} + +#define NUM_SPHERES 13 // max no. +#define NUM_MATERIALS 11 + +Sphere spheres[NUM_SPHERES]; +Material mats[NUM_MATERIALS]; + +uint sphere_count = NUM_SPHERES; +float hit_sphere(in Sphere sphere, Ray ray) { + float t = -1.0; + vec3 rc = ray.origin - sphere.pos; + float c = dot(rc, rc) - (sphere.r * sphere.r); + float b = dot(ray.dir, rc); + float d = b*b - c; + if(d > 0.0) { + t = -b - sqrt(abs(d)); + return t; + } else { + return -1.0; + } +} + +float intersect_scene(inout uint sphere, Ray ray) { + float mint = T_MAX; + sphere = 0; + for(int i = 0; i < sphere_count; ++i) { + float t = hit_sphere(spheres[i], ray); + if(t > 0 && t < mint) { + mint = t; + sphere = i; + } + } + return (mint >= T_MAX) ? -1 : mint; +} + +Ray gen_ray(inout uint rng_state) { + float focal_len = sqrt(screen_size.x*screen_size.x + screen_size.y*screen_size.y) / ( 2*tan( radians(45/2) ) ); + vec2 jitter = vec2(rand(rng_state), rand(rng_state)); + vec2 pixel_center = gl_FragCoord.xy + jitter; + vec2 uv = pixel_center / screen_size.xy; + uv = uv * 2.0 - 1.0; + vec4 origin = inv_cam_view * vec4(0,0,0,1); + vec4 target = inv_cam_proj * vec4(uv.x, uv.y, focal_len, 1); + vec4 dir = inv_cam_view * vec4(normalize(target.xyz), 0); + Ray ray; + ray.dir = dir.xyz; + ray.origin = origin.xyz; + return ray; +} + +void get_effect(inout Material mat, in vec3 inter_p) { + switch(mat.effect) { + case 0: + break; + case 1: + if(rand3D(round(inter_p)) > 0.70) { // pixel light effect + mat.emmitence = 0.5; + } else { + mat.emmitence = 0.075; + } + break; + case 2: + if(inter_p.y - floor(inter_p.y) < 0.5) { // stripe light effect + mat.emmitence = 0.5; + } else { + mat.emmitence = 0.2; + } + default: + break; + } +} + +// not physically accurate, but still looks good +vec3 brdf(inout Ray in_ray, Material mat, vec3 normal, inout uint rng_state ){ + float spec_prob = mat.spec_chance; + if(spec_prob > 0) { + spec_prob = fresnel_coe(1.0, mat.ior, in_ray.dir, normal, mat.spec_chance, 1.0); + } + spec_prob = (rand(rng_state) < spec_prob) ? 1.0f : 0.0f; + vec3 diff_ray = normalize(normal + rand_vec(rng_state)); + vec3 spec_ray = reflect(in_ray.dir, normal); + spec_ray = normalize(mix(spec_ray, diff_ray, mat.roughness*mat.roughness)); + in_ray.dir = mix(diff_ray, spec_ray, spec_prob); // glossy outgoing ray + return mix(mat.albedo, mat.specular, spec_prob); +} + +vec3 calc_sample(uint max_bounces, inout uint rng_state) { + vec3 accumulator = vec3(0); + vec3 mask = vec3(1); + + Ray ray = gen_ray(rng_state); + uint id; + for(int b = 0; b < max_bounces; ++b) { + float t = intersect_scene(id, ray); + if(t < EPS) break; + vec3 inter_p = ray.origin + ray.dir*t; + + Material mat = mats[spheres[id].mat]; + + get_effect(mat, inter_p); + + vec3 normal = normalize(inter_p - spheres[id].pos); + ray.origin = inter_p; + + vec3 throughput = brdf(ray, mat, normal, rng_state); + accumulator += (mask * mat.emmitence * mat.albedo); + mask *= throughput; + + // russian roulette + float p = max(mask.r, max(mask.g, mask.b)); + if(rand(rng_state) > p) + break; + mask *= 1.0/p; // weight samples based on chance of terminating + } + return accumulator; +} + +#define WHITE_DIFF 0 +#define MIRROR 1 +#define PIXEL_LIGHT 2 +#define RED_DIFF 3 +#define GREEN_DIFF 4 +#define GOLD_SPEC 5 +#define WHITE_SPEC 6 +#define EMITTING_SPEC 7 +#define SILVER_SPEC 8 +#define STRIPED_LIGHT 9 +#define BLUE_DIFF 10 + +void load_mats() { + { // init scene + mats[0].albedo = vec3(1); + mats[0].emmitence = 0; + mats[0].effect = 0; + mats[0].specular = vec3(0); + mats[0].spec_chance = 0; + mats[0].roughness = 0; + mats[0].ior = 1; + + mats[1].albedo = vec3(1); + mats[1].emmitence = 0; + mats[1].effect = 0; + mats[1].specular = vec3(1); + mats[1].spec_chance = 1.0; + mats[1].roughness = 0.05; + mats[1].ior = 1; + + mats[2].albedo = vec3(1); + mats[2].emmitence = 0.25; + mats[2].effect = 1; + mats[2].specular = vec3(0); + mats[2].spec_chance = 0; + mats[2].roughness = 0; + mats[2].ior = 1; + + mats[3].albedo = vec3(0.7,0,0); + mats[3].emmitence = 0; + mats[3].effect = 0; + mats[3].specular = vec3(0); + mats[3].spec_chance = 0; + mats[3].roughness = 0; + mats[3].ior = 1; + + mats[4].albedo = vec3(0,0.7,0); + mats[4].emmitence = 0; + mats[4].effect = 0; + mats[4].specular = vec3(0); + mats[4].spec_chance = 0; + mats[4].roughness = 0; + mats[4].ior = 1; + + mats[5].albedo = vec3(212.0/255.0,175.0/255.0,55.0/255.0); + mats[5].emmitence = 0; + mats[5].effect = 0; + mats[5].specular = mats[5].albedo; + mats[5].spec_chance = 0.7; + mats[5].roughness = 0.5; + mats[5].ior = 1; + + mats[6].albedo = vec3(0.7,0.7,0.7); + mats[6].emmitence = 0; + mats[6].effect = 1; + mats[6].specular = vec3(1); + mats[6].spec_chance = 0.5; + mats[6].roughness = 0.2; + mats[6].ior = 1; + + mats[7].albedo = vec3(1); + mats[7].emmitence = 0.2; + mats[7].effect = 0; + mats[7].specular = vec3(1); + mats[7].spec_chance = 0.5; + mats[7].roughness = 0.2; + mats[7].ior = 1; + + mats[8].albedo = vec3(192.0/255.0,192.0/255.0,192.0/255.0); + mats[8].emmitence = 0; + mats[8].effect = 0; + mats[8].specular = mats[8].albedo; + mats[8].spec_chance = 0.7; + mats[8].roughness = 0.5; + mats[8].ior = 1; + + mats[9].albedo = vec3(1); + mats[9].emmitence = 0.3; + mats[9].effect = 2; + mats[9].specular = vec3(0); + mats[9].spec_chance = 0; + mats[9].roughness = 0; + mats[9].ior = 1; + + mats[10].albedo = vec3(0,0,0.7); + mats[10].emmitence = 0; + mats[10].effect = 0; + mats[10].specular = vec3(0); + mats[10].spec_chance = 0; + mats[10].roughness = 0; + mats[10].ior = 1; + } +} + +void load_scene1() { + const float room_height = 18.0f; + const float room_width = 18.0f; + const float wall_r = 1000; + + spheres[0].pos = vec3(0, -wall_r, 0); // bottom floor + spheres[0].r = wall_r; + spheres[0].mat = PIXEL_LIGHT; + + spheres[1].pos = vec3(0, room_height+wall_r, 0); // top ceiling + spheres[1].r = wall_r; + spheres[1].mat = MIRROR; + + spheres[2].pos = vec3(room_width/2 + wall_r, 0, 0); // side wall + spheres[2].r = wall_r; + spheres[2].mat = MIRROR; + + spheres[3].pos = vec3(-room_width/2 - wall_r, 0, 0); // side wall + spheres[3].r = wall_r; + spheres[3].mat = MIRROR; + + spheres[4].pos = vec3(0, 0, room_width/2 + wall_r); // front wall + spheres[4].r = wall_r; + spheres[4].mat = MIRROR; + + spheres[5].pos = vec3(0, 0, -room_width/2 - wall_r); // back wall + spheres[5].r = wall_r; + spheres[5].mat = MIRROR; + + spheres[6].pos = vec3(0, 1, 0); + spheres[6].r = 1; + spheres[6].mat = MIRROR; + + spheres[7].pos = vec3(0, 5, 0); + spheres[7].r = 2; + spheres[7].mat = MIRROR; + + spheres[8].pos = vec3(0, 9, 0); + spheres[8].r = 1; + spheres[8].mat = MIRROR; + + spheres[9].pos = vec3(4, 5, 0); + spheres[9].r = 1; + spheres[9].mat = MIRROR; + + spheres[10].pos = vec3(-4, 5, 0); + spheres[10].r = 1; + spheres[10].mat = MIRROR; + + spheres[11].pos = vec3(0, 5, 4); + spheres[11].r = 1; + spheres[11].mat = MIRROR; + + spheres[12].pos = vec3(0, 5, -4); + spheres[12].r = 1; + spheres[12].mat = MIRROR; + + sphere_count = 13; +} + +void load_scene2() { + const float room_height = 15.0f; + const float room_width = 15.0f; + const float wall_r = 1000; + + mats[2].albedo = vec3(0.5, 0.05, 0.05); // for red light + + spheres[0].pos = vec3(0, -wall_r, 0); // bottom floor + spheres[0].r = wall_r; + spheres[0].mat = WHITE_DIFF; + + spheres[1].pos = vec3(0, room_height+wall_r, 0); // top ceiling + spheres[1].r = wall_r; + spheres[1].mat = WHITE_DIFF; + + spheres[2].pos = vec3(room_width/2 + wall_r, 0, 0); // side wall + spheres[2].r = wall_r; + spheres[2].mat = PIXEL_LIGHT; + + spheres[3].pos = vec3(-room_width/2 - wall_r, 0, 0); // side wall + spheres[3].r = wall_r; + spheres[3].mat = EMITTING_SPEC; + + spheres[4].pos = vec3(0, 0, room_width/2 + wall_r); // front wall + spheres[4].r = wall_r; + spheres[4].mat = WHITE_DIFF; + + spheres[5].pos = vec3(0, 0, -room_width/2 - wall_r); // back wall + spheres[5].r = wall_r; + spheres[5].mat = WHITE_DIFF; + + spheres[6].pos = vec3(0, 1, 4); + spheres[6].r = 1; + spheres[6].mat = WHITE_DIFF; + + spheres[7].pos = vec3(0, 2, -4); + spheres[7].r = 2; + spheres[7].mat = WHITE_DIFF; + + sphere_count = 8; +} + +void load_scene3() { + const float room_height = 20.0f; + const float room_width = 30.0f; + const float wall_r = 1000; + + spheres[0].pos = vec3(0, -wall_r, 0); // bottom floor + spheres[0].r = wall_r; + spheres[0].mat = WHITE_DIFF; + + spheres[1].pos = vec3(0, room_height+wall_r, 0); // top ceiling + spheres[1].r = wall_r; + spheres[1].mat = WHITE_DIFF; + + spheres[2].pos = vec3(room_width/2 + wall_r, 0, 0); // side wall + spheres[2].r = wall_r; + spheres[2].mat = PIXEL_LIGHT; + + spheres[3].pos = vec3(-room_width/2 - wall_r, 0, 0); // side wall + spheres[3].r = wall_r; + spheres[3].mat = GOLD_SPEC; + + spheres[4].pos = vec3(0, 0, room_width/2 + wall_r); // front wall + spheres[4].r = wall_r; + spheres[4].mat = SILVER_SPEC; + + spheres[5].pos = vec3(0, 0, -room_width/2 - wall_r); // back wall + spheres[5].r = wall_r; + spheres[5].mat = SILVER_SPEC; + + spheres[6].pos = vec3(0, 2, -8); + spheres[6].r = 2; + spheres[6].mat = SILVER_SPEC; + + spheres[7].pos = vec3(0, 3, -1); + spheres[7].r = 3; + spheres[7].mat = GOLD_SPEC; + + spheres[8].pos = vec3(0, 4, 8); + spheres[8].r = 4; + spheres[8].mat = MIRROR; + + sphere_count = 9; +} + +void load_scene4() { + const float room_height = 30.0f; + const float room_width = 30.0f; + const float wall_r = 1000; + + spheres[0].pos = vec3(0, -wall_r, 0); // bottom floor + spheres[0].r = wall_r; + spheres[0].mat = RED_DIFF; + + spheres[1].pos = vec3(0, room_height+wall_r, 0); // top ceiling + spheres[1].r = wall_r; + spheres[1].mat = BLUE_DIFF; + + spheres[2].pos = vec3(room_width/2 + wall_r, 0, 0); // side wall + spheres[2].r = wall_r; + spheres[2].mat = MIRROR; + + spheres[3].pos = vec3(-room_width/2 - wall_r, 0, 0); // side wall + spheres[3].r = wall_r; + spheres[3].mat = MIRROR; + + spheres[4].pos = vec3(0, 0, room_width/2 + wall_r); // front wall + spheres[4].r = wall_r; + spheres[4].mat = STRIPED_LIGHT; + + spheres[5].pos = vec3(0, 0, -room_width/2 - wall_r); // back wall + spheres[5].r = wall_r; + spheres[5].mat = WHITE_DIFF; + + spheres[6].pos = vec3(0, 4, 0); + spheres[6].r = 4; + spheres[6].mat = MIRROR; + + spheres[7].pos = vec3(0, 12, 0); + spheres[7].r = 4; + spheres[7].mat = MIRROR; + + spheres[8].pos = vec3(0, 20, 0); + spheres[8].r = 4; + spheres[8].mat = MIRROR; + + sphere_count = 9; +} + +void main() { + const uint SPF = 15; // sample per frame + const uint MAX_DEPTH = 7; + const float gamma = 2.2; + + load_mats(); + + load_scene1(); + + //load_scene2(); + + //load_scene3(); + + //load_scene4(); + + vec3 pixel_color = vec3(0); + uint seed = uint(screen_size.z+1)*init_rand_seed(uint(dot(gl_FragCoord.xz, vec2(gl_FragCoord.y))), uint(dot(gl_FragCoord.zy, vec2(gl_FragCoord.x)))); + for(int s = 0; s < SPF; ++s) { + pixel_color += calc_sample(MAX_DEPTH, seed); + } + pixel_color /= float(SPF); + + vec4 prev_color = imageLoad(out_img, ivec2(gl_FragCoord.xy)); + vec3 last_frame_color = prev_color.xyz * float(screen_size.z); + pixel_color += last_frame_color; + pixel_color /= float(screen_size.z+1); + imageStore(out_img, ivec2(gl_FragCoord.xy), vec4(pixel_color, 1)); + + pixel_color = ACESFilm(pixel_color); + pixel_color = LinearToSRGB(pixel_color); + frag_color = vec4(pixel_color, 1); +} diff --git a/Runner/vert.glsl b/Runner/vert.glsl new file mode 100644 index 0000000..e9504d9 --- /dev/null +++ b/Runner/vert.glsl @@ -0,0 +1,12 @@ +#version 420 core + +#extension GL_EXT_shader_image_load_store : require + +layout(location = 0) in vec3 pos; + +uniform mat4 cam_proj; +uniform mat4 cam_view; + +void main() { + gl_Position = vec4(pos.xyz, 1.0); +} diff --git a/index.html b/index.html index 432c007..6aa9991 100644 --- a/index.html +++ b/index.html @@ -4,6 +4,6 @@ - + \ No newline at end of file