From 8d0937cb5079249ddff69e50701ef623f57b8c1b Mon Sep 17 00:00:00 2001 From: ktyl Date: Thu, 29 Jul 2021 23:39:04 +0100 Subject: [PATCH] preprocess shaders --- makefile | 40 +- ppp.py | 66 +++ res/container.jpg | 3 - res/shader/compute.glsl | 107 ---- res/shader/include/sphere.glsl | 8 + res/shader/{ => quad}/shader.frag | 0 res/shader/{ => quad}/shader.vert | 0 {src/_shader => res/shader}/root/compute.glsl | 8 +- res/shader/rt.compute | 506 ------------------ res/tex.png | 3 - src/_shader/root/rt.compute | 506 ------------------ src/_shader/root/shader.frag | 12 - src/_shader/root/shader.vert | 14 - src/main.c | 12 +- todo.md | 6 +- 15 files changed, 110 insertions(+), 1181 deletions(-) create mode 100644 ppp.py delete mode 100644 res/container.jpg delete mode 100644 res/shader/compute.glsl create mode 100644 res/shader/include/sphere.glsl rename res/shader/{ => quad}/shader.frag (100%) rename res/shader/{ => quad}/shader.vert (100%) rename {src/_shader => res/shader}/root/compute.glsl (97%) delete mode 100644 res/shader/rt.compute delete mode 100644 res/tex.png delete mode 100644 src/_shader/root/rt.compute delete mode 100644 src/_shader/root/shader.frag delete mode 100644 src/_shader/root/shader.vert diff --git a/makefile b/makefile index e60cffb..aa2b4e9 100644 --- a/makefile +++ b/makefile @@ -2,11 +2,22 @@ SRC_DIR = src BIN_DIR = bin RES_DIR = res -SHADER_DIR = $(SRC_DIR)/_shader -SHADER_ROOT_DIR = $(SHADER_DIR)/root -SHADER_INCLUDE_DIR = $(SHADER_DIR)/common +SHADER_DIR = $(RES_DIR)/shader +SHADER_QUAD_DIR = $(SHADER_DIR)/quad +SHADER_ROOT_DIR = $(SHADER_DIR)/root +SHADER_INCLUDE_DIR = $(SHADER_DIR)/include +SHADER_TARGET_DIR = $(BIN_DIR)/$(RES_DIR) + +# find files in SHADER_ROOT_DIR +# top level compute shader programs +SHADERS = $(shell find $(SHADER_ROOT_DIR) -wholename "$(SHADER_ROOT_DIR)*.glsl") +# find files in SHADER_INCLUDE_DIR +# small chunks of shader code, included repeatedly in the top-level programs +SHADER_INCLUDES = $(shell find $(SHADER_INCLUDE_DIR) -name *.glsl) +SHADER_TARGETS = $(SHADERS:$(SHADER_ROOT_DIR)/%.glsl=$(SHADER_TARGET_DIR)/%.compute) TARGET = $(BIN_DIR)/oglc + CC = gcc LIBS = `pkg-config --static --libs glew sdl2` CFLAGS = -I$(SRC_DIR) -Wall @@ -14,24 +25,20 @@ CFLAGS = -I$(SRC_DIR) -Wall SRC = $(shell find $(SRC_DIR) -name *.c) OBJ = $(SRC:%.c=%.o) -# find files in SHADER_ROOT_DIR -# top level compute shader programs -SHADERS = $(shell find $(SHADER_ROOT_DIR) -name *.glsl) - -# find files in SHADER_INCLUDE_DIR -# small chunks of shader code, included repeatedly in the top-level programs -SHADER_INCLUDES = $(shell find $(SHADER_INCLUDE_DIR) -name *.glsl) - # create dirs if they dont exist _dummy := $(shell mkdir -p $(BIN_DIR)) -$(TARGET): $(OBJ) - # preprocess shaders and store results in bin/res/shader/ under root name - foreach root,$(SHADER_ROOT_DIR),$(echo $(root)) +$(TARGET): $(OBJ) $(SHADER_TARGETS) + mkdir -p $(BIN_DIR) + mkdir -p $(SHADER_TARGET_DIR) + cp $(shell find $(SHADER_QUAD_DIR) -wholename "$(SHADER_QUAD_DIR)/*") $(SHADER_TARGET_DIR) + $(CC) $(CFLAGS) -o $@ $(OBJ) $(LIBS) - $(CC) $(CFLAGS) -o $@ $^ $(LIBS) - cp -r $(RES_DIR) $(BIN_DIR) +$(SHADER_TARGET_DIR)/%.compute: $(SHADER_ROOT_DIR)/%.glsl $(SHADER_INCLUDES) + mkdir -p $(SHADER_TARGET_DIR) + python ppp.py $< $(SHADER_INCLUDES) > $@ +# how to make a .o out of a .c %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< @@ -42,5 +49,4 @@ clean: run: $(TARGET) $(TARGET) - .PHONY: run clean diff --git a/ppp.py b/ppp.py new file mode 100644 index 0000000..4c47592 --- /dev/null +++ b/ppp.py @@ -0,0 +1,66 @@ +import sys +import os.path + +err = False +paths = [] + +def print_usage(): + print("\nusage: python ppp.py ROOT TEMPLATES [...]") + +# check arguments +argc = len(sys.argv) +if argc < 3: + print_usage() + sys.exit(1) + +# figure out src dir from first include shader path root argument +sep="/" +inc_start_idx = 2 +inc_end_idx = argc - 1 +src_dir = sep.join(sys.argv[2].split(sep)[:-1]) + +def preprocess_file(path): + lines=0 + + with open(path) as f: + content = f.readlines() + content = [x.strip() for x in content] + lines=len(content) + + for line in content: + + directive = "#include" + if line.startswith(directive): + include_path = line.split(" ")[1] + + # prepend directory + include_path = "/".join([src_dir, include_path]) + + preprocess_file(include_path) + continue + + print(line) + + +for i in range(1,argc): + path = sys.argv[i] + + if not os.path.isfile(path): + + print(path + " is not a file") + err = True + continue + + if path in paths: + # ignore duplicates + continue + + paths.append(path) + +if err: + print_usage() + sys.exit(1) + +preprocess_file(sys.argv[1]) + +sys.exit(0) diff --git a/res/container.jpg b/res/container.jpg deleted file mode 100644 index f04776f..0000000 --- a/res/container.jpg +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:126baccff187648acfad78c57560035f5c783cc6ec92a37a93a5b1edc97af10e -size 184939 diff --git a/res/shader/compute.glsl b/res/shader/compute.glsl deleted file mode 100644 index 4e822f1..0000000 --- a/res/shader/compute.glsl +++ /dev/null @@ -1,107 +0,0 @@ -#version 430 - -layout (location = 1) uniform vec4 t; - -layout(local_size_x = 1, local_size_y = 1) in; // size of local work group - 1 pixel -layout(rgba32f, binding = 0) uniform image2D img_output; // rgba32f defines internal format, image2d for random write to output texture - -const float INF = 1000000.0f; - -struct Sphere -{ - vec3 center; - float radius; -}; - -struct Ray -{ - vec3 origin; - vec3 direction; -}; - -struct RayHit -{ - vec3 position; - float distance; - vec3 normal; -}; - -void intersectSphere(Ray ray, inout RayHit bestHit, Sphere sphere) -{ - vec3 d = ray.origin-sphere.center; - float p1 = -dot(ray.direction,d); - float p2sqr = p1*p1-dot(d,d)+sphere.radius*sphere.radius; - - 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-sphere.center); - } -} - -Ray createCameraRay(vec2 uv) -{ - // transform -1..1 -> 0..1 - uv = uv*0.5+0.5; - //uv.x=1-uv.x; - - // transform camera origin to world space - // TODO: c2w matrix!! for now we just assume the camera is at the origin - // float3 origin = mul(_CameraToWorld, float4(0.0,0.0,0.0,1.0)).xyz; - - // TODO: offset from centre of the lens for depth of field - // float2 rd = _CameraLensRadius * randomInUnitDisk(); - // float3 offset = _CameraU * rd.x + _CameraV * rd.y; - - // ... - - float max_x = 5.0; - float max_y = 5.0; - - Ray ray; - ray.origin = vec3(uv.x * max_x, uv.y * max_y, 0.0); - ray.direction = vec3(0.0,0.0,1.0); // ortho forwards - - return ray; -} - -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); - - // set up ray based on pixel position, project it forward with an orthographic projection - ivec2 dims = imageSize(img_output); // fetch image dimensions - vec2 uv; - uv.x = (float(pixel_coords.x * 2 - dims.x) / dims.x) * dims.x/dims.y; // account for aspect ratio - uv.y = (float(pixel_coords.y * 2 - dims.y) / dims.y); - - Ray ray = createCameraRay(uv); - - RayHit hit; - hit.position = vec3(0.0,0.0,0.0); - hit.distance = INF; - hit.normal = vec3(0.0,0.0,0.0); - - Sphere sphere; - sphere.center = vec3(0.0,0.0,10.0); - sphere.radius = 3.0+t.y; - - // ray-sphere intersection - intersectSphere(ray, hit, sphere); - - if (hit.distance < INF) - { - pixel = vec4(t.y,1.0-t.y,1.0,1.0); - } - - // output to a specific pixel in the image - imageStore(img_output, pixel_coords, pixel); -} diff --git a/res/shader/include/sphere.glsl b/res/shader/include/sphere.glsl new file mode 100644 index 0000000..e991daa --- /dev/null +++ b/res/shader/include/sphere.glsl @@ -0,0 +1,8 @@ +struct Sphere +{ + vec3 center; + float radius; + vec3 albedo; + vec3 specular; + vec3 emission; +}; diff --git a/res/shader/shader.frag b/res/shader/quad/shader.frag similarity index 100% rename from res/shader/shader.frag rename to res/shader/quad/shader.frag diff --git a/res/shader/shader.vert b/res/shader/quad/shader.vert similarity index 100% rename from res/shader/shader.vert rename to res/shader/quad/shader.vert diff --git a/src/_shader/root/compute.glsl b/res/shader/root/compute.glsl similarity index 97% rename from src/_shader/root/compute.glsl rename to res/shader/root/compute.glsl index 4e822f1..16ed4bc 100644 --- a/src/_shader/root/compute.glsl +++ b/res/shader/root/compute.glsl @@ -7,11 +7,7 @@ layout(rgba32f, binding = 0) uniform image2D img_output; // rgba32f defines i const float INF = 1000000.0f; -struct Sphere -{ - vec3 center; - float radius; -}; +#include sphere.glsl struct Ray { @@ -47,7 +43,7 @@ void intersectSphere(Ray ray, inout RayHit bestHit, Sphere sphere) Ray createCameraRay(vec2 uv) { // transform -1..1 -> 0..1 - uv = uv*0.5+0.5; + //uv = uv*0.5+0.5; //uv.x=1-uv.x; // transform camera origin to world space diff --git a/res/shader/rt.compute b/res/shader/rt.compute deleted file mode 100644 index 3974d70..0000000 --- a/res/shader/rt.compute +++ /dev/null @@ -1,506 +0,0 @@ -// Each #kernel tells which function to compile; you can have many kernels -#pragma kernel CSMain - -struct Sphere -{ - float3 position; - float radius; - float3 albedo; - float3 specular; - float3 emission; -}; - -struct Tube -{ - float3 position; - float3 axis; - float radius; - float height; - float thickness; -}; - -struct Unit -{ - float3 position; - int team; - int selected; -}; - -// Create a RenderTexture with enableRandomWrite flag and set it -// with cs.SetTexture -RWTexture2D Result; -float2 _Pixel; -float _Seed; -float _EmissionScale; -int _Bounces; -int _SamplesPerPixel; - -// camera -float2 _Resolution; -float4x4 _CameraToWorld; -float4x4 _CameraInverseProjection; -float3 _CameraW; -float3 _CameraU; -float3 _CameraV; -float3 _CameraHorizontal; -float3 _CameraVertical; -float3 _CameraLowerLeftCorner; -float _CameraLensRadius; -float _CameraFocusDistance; - -// environment -float _GroundHeight; -float3 _GroundColor; -float _SkyHeight; -float _SkyHoleRadius; -float3 _SkyColor; - -int _ActiveSpheres; -int _ActiveTubes; - -int _ActiveUnits; -float3 _UnitColor; -float _UnitRadius; - -StructuredBuffer _Units; -StructuredBuffer _Tubes; -StructuredBuffer _Spheres; - -#define GROUP_SIZE 32 - -static const float PI = 3.14159265f; -static const float BIG = 1000000.0f; // not infinity but close enough - -static const int MAT_LAMBERT = 0; -static const int MAT_DIELECTRIC = 1; - -struct Ray -{ - float3 origin; - float3 direction; - float3 energy; -}; - -Ray createRay(float3 origin, float3 direction) -{ - Ray ray; - ray.origin = origin; - ray.direction = direction; - ray.energy = float3(1.0f, 1.0f, 1.0f); - return ray; -} - -struct RayHit -{ - float3 position; - float distance; - float3 normal; - float3 albedo; - float3 specular; - float3 emission; -}; - -RayHit createRayHit() -{ - RayHit hit; - hit.position = float3(0.0f, 0.0f, 0.0f); - hit.distance = BIG; - hit.normal = float3(0.0f, 0.0f, 0.0f); - hit.albedo = float3(0.0f, 0.0f, 0.0f); - hit.specular = float3(0.0f, 0.0f, 0.0f); - hit.emission = float3(0.0f, 0.0f, 0.0f); - return hit; -} - -float rand() -{ - float result = frac(sin(_Seed / 100.0f * dot(_Pixel, float2(12.9898f, 78.233f))) * 43758.5453f); - _Seed += 1.0f; - return result; -} - -float sdot(float3 x, float3 y, float f = 1.0f) -{ - return saturate(dot(x, y) * f); -} - -float3x3 getTangentSpace(float3 normal) -{ - // helper vector for the cross product - float3 helper = float3(1, 0, 0); - if (abs(normal.x) > 0.99f) - { - helper = float3(0, 0, 1); - } - - // generate vectors - float3 tangent = normalize(cross(normal, helper)); - float3 binormal = normalize(cross(normal, tangent)); - return float3x3(tangent, binormal, normal); -} - -float3 sampleHemisphere(float3 normal) -{ - // uniformly sample hemisphere direction - float cosTheta = rand(); - float sinTheta = sqrt(max(0.0f, 1.0f - cosTheta * cosTheta)); - float phi = 2 * PI * rand(); - float3 tangentSpaceDir = float3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta); - - // transform direction to world space - return mul(tangentSpaceDir, getTangentSpace(normal)); -} - -float2 randomInUnitDisk() -{ - // pick a random radius and angle then convert to cartesian - float r = rand(); - float theta = rand() * 2 * PI; - return float2(cos(theta), sin(theta)) * r; -} - -Ray createCameraRay(float2 uv) -{ - // transform -1..1 -> 0..1 - uv = uv * 0.5 + 0.5; - uv.x = 1 - uv.x; - - // transform the camera origin to world space - float3 origin = mul(_CameraToWorld, float4(0.0f, 0.0f, 0.0f, 1.0f)).xyz; - - // offset from centre of the lens for depth of field - float2 rd = _CameraLensRadius * randomInUnitDisk(); - float3 offset = _CameraU * rd.x + _CameraV * rd.y; - - origin += offset; - - // invert the perspective projection of the view-space position - float3 direction = mul(_CameraInverseProjection, float4(uv, 0.0f, 1.0f)).xyz; - - // transform the direction from camera to world space and normalize - direction = mul(_CameraToWorld, float4(direction, 0.0f)).xyz; - - direction = _CameraLowerLeftConer - + uv.x * _CameraHorizontal - + uv.y * _CameraVertical - - origin; - - // direction = mul(_CameraInverseProjection, float4(direction, 0)).xyz; - - direction = normalize(direction); - return createRay(origin, direction); -} - - -void intersectSphere(Ray ray, inout RayHit bestHit, Sphere sphere) -{ - // calculate distance along the ray where the sphere is intersected - float3 d = ray.origin - sphere.position; - float p1 = -dot(ray.direction, d); - float p2sqr = p1 * p1 - dot(d, d) + sphere.radius * sphere.radius; - - 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 - sphere.position); - bestHit.albedo = sphere.albedo; - bestHit.specular = sphere.specular; - bestHit.emission = sphere.emission; - } -} - -float intersectPlane(Ray ray, float3 p, float3 normal) -{ - float denom = dot(normal, ray.direction); - - if (abs(denom) > 0.0001) - { - float t = dot(p - ray.origin, normal) / denom; - if (t >= 0) return t; - } - return -1; -} - -// https://www.iquilezles.org/www/articles/intersectors/intersectors.htm -// cylinder defined in extremes pa and pb, and radius ra -float4 intersectCylinder(Ray ray, float3 pa, float3 pb, float ra, bool inner) -{ - float3 ro = ray.origin; - float3 rd = ray.direction; - - // central axis - float3 ca = pb - pa; - // eye to base - float3 oc = ro - pa; - - // dot products - float caca = dot(ca, ca); - float card = dot(ca, rd); - float caoc = dot(ca, oc); - - // find intersects - float a = caca - card * card; - float b = caca * dot(oc, rd) - caoc * card; - float c = caca * dot(oc, oc) - caoc * caoc - ra * ra * caca; - float h = b * b - a * c; - - if (h < 0.0) return float4(-1, 0, 0, 0); // no intersection - - h = sqrt(h); - h = inner?-h:h; - float t = (-b - h) / a; - - // body - float y = caoc + t * card; - if (y > 0.0 && y < caca) return float4(t, (oc+t*rd - ca*y/caca) / ra); - - // caps - t = ((y < 0.0 ? 0.0 : caca) - caoc) / card; - if (abs(b + a * t) < h) return float4(t, ca * sign(y) / caca); - - return float4(-1, 0, 0, 0); // no intersection -} - -float sdSegment(float3 p, float3 a, float3 b, float r) -{ - float3 pa = p - a, ba = b - a; - float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return length(pa - ba * h) - r; -} - -float opSubtraction(float d1, float d2) { return max(-d1, d2); } - -void intersectTube(Ray ray, inout RayHit bestHit, Tube tube) -{ - // TODO: inner tube - - float height = tube.height; - - float3 axis = normalize(tube.axis); - - float3 pa = tube.position + axis * -height * 0.5; - float3 pb = tube.position + axis * height * 0.5; - - float r_inner = (tube.radius-tube.thickness)/tube.radius; - - // where the ray hit the outer surface - float4 outerHit = intersectCylinder(ray, pa, pb, tube.radius, false); - // outerHit = float4(-1,0,0,0); - // where we hit the inner surface - float4 innerHit = intersectCylinder(ray, pa, pb, tube.radius * r_inner, true); - // float4 innerHit = float4(-1,0,0,0); - // innerHit.yzw *= -1; - - if (outerHit.x < 0 && innerHit.x < 0) return; - - float3 pos_outer = ray.origin + outerHit.x * ray.direction; - float axis_distance = sdSegment(pos_outer, pa, pb, 0); - - float t = bestHit.distance; - - // hit the inner surface - if (innerHit.x > 0 && innerHit.x < bestHit.distance) - { - t = innerHit.x; - - bestHit.normal = normalize(innerHit.yzw); - bestHit.position = ray.origin + t * ray.direction; - bestHit.distance = t; - - bestHit.albedo = float3(.5, .5, .5); - bestHit.emission = float3(0, 0, 0); - bestHit.specular = float3(1, 1, 1); - } - - // hit the outer surface - if (outerHit.x > 0 && outerHit.x < bestHit.distance && axis_distance > tube.radius*r_inner) - { - t = outerHit.x; - - bestHit.normal = normalize(outerHit.yzw); - bestHit.position = ray.origin + t * ray.direction; - bestHit.distance = t; - - bestHit.albedo = float3(.5, .5, .5); - bestHit.emission = float3(0, 0, 0); - bestHit.specular = float3(1, 1, 1); - } -} - -void intersectGroundPlane(inout Ray ray, inout RayHit bestHit) -{ - float3 albedo = _GroundColor; - float3 specular = float3(0, 0, 0); - - // calculate distance along the ray where the ground plane is intersected - float t = -(ray.origin.y - _GroundHeight) / ray.direction.y; - - if (t > 0 && t < bestHit.distance) - { - bestHit.distance = t; - bestHit.position = ray.origin + t * ray.direction; - bestHit.normal = float3(0.0f, 1.0f, 0.0f); - bestHit.albedo = albedo; - bestHit.specular = specular; - bestHit.emission = float3(0, 0, 0); - } -} - -void intersectCeilingPlane(inout Ray ray, inout RayHit bestHit) -{ - float albedo = _SkyColor; - float3 specular = float3(0, 0, 0); - - // ignore plane if the ray is coming from above - if (ray.direction.y < 0) return; - - float t = -(ray.origin.y - _SkyHeight) / ray.direction.y; - float3 p = ray.origin + ray.direction * t; - - if (length(p.xz) < _SkyHoleRadius) return; - - if (t > 0 && t < bestHit.distance) - { - bestHit.distance = t; - bestHit.position = ray.origin + t * ray.direction; - bestHit.normal = float3(0.0f, -1.0f, 0.0f); - bestHit.albedo = albedo; - bestHit.specular = specular; - bestHit.emission = float3(0, 0, 0); - } -} - -void intersectWall(inout Ray ray, inout RayHit bestHit) -{ - // ignore collision if ray's angle is steep or negative - float a = dot(float3(0, 1, 0), ray.direction); - if (a > 0.2 || a < 0) return; - - Sphere sphere; - sphere.radius = BIG - 1; - sphere.albedo = float3(1, 1, 1) * 1.98; - sphere.specular = float3(0, 0, 0); - sphere.emission = float3(0, 0, 0); - sphere.position = float3(0, 0, 0); - - intersectSphere(ray, bestHit, sphere); -} - -RayHit trace(Ray ray) -{ - RayHit bestHit = createRayHit(); - - intersectWall(ray, bestHit); - intersectGroundPlane(ray, bestHit); - intersectCeilingPlane(ray, bestHit); - - uint numSpheres, numTubes, stride; - - // celestial bodies - // _Spheres.GetDimensions(_ActiveSpheres, stride); - for (uint i = 0; i < _ActiveSpheres; i++) - { - intersectSphere(ray, bestHit, _Spheres[i]); - } - - // _Tubes.GetDimensions(numTubes, stride); - for (uint i = 0; i < _ActiveTubes; i++) - { - intersectTube(ray, bestHit, _Tubes[i]); - } - - if (_ActiveUnits > 0) - { - // units - _Units.GetDimensions(numSpheres, stride); - for (uint i = 0; i < _ActiveUnits; i++) - { - Unit unit = _Units[i]; - - float3 color = float3 - (lerp(1, 0, unit.team), - 0, - lerp(0, 1, unit.team)); - - Sphere s; - s.albedo = color; - s.emission = color * unit.selected; - s.specular = float3(0, 0, 0); - s.radius = _UnitRadius; - s.position = unit.position; - - intersectSphere(ray, bestHit, s); - } - } - - return bestHit; -} - -float3 scatter_lambert(inout Ray ray, RayHit hit) -{ - ray.origin = hit.position + hit.normal * 0.001f; - ray.direction = sampleHemisphere(hit.normal); - ray.energy *= 2 * hit.albedo * sdot(hit.normal, ray.direction); - return 0.0f; -} - -float3 shade(inout Ray ray, RayHit hit) -{ - if (any(hit.emission)) return hit.emission; - - if (hit.distance < BIG) - { - return scatter_lambert(ray, hit) + hit.emission; - } - else - { - ray.energy = 0.0f; - - // float theta = acos(ray.direction.y) / -PI; - // float phi = atan2(ray.direction.x, -ray.direction.z) / -PI * 0.5f; - - return _SkyColor; - } -} - -[numthreads(GROUP_SIZE,GROUP_SIZE,1)] -void CSMain(uint3 id : SV_DispatchThreadID) -{ - _Pixel = id.xy; - - // get dimensions of render texture - uint width, height; - Result.GetDimensions(width, height); - - // transform pixel to -1, 1 range - float2 uv = float2(id.xy / float2(width, height) * 2.0f - 1.0f); - uv.x *= _Resolution.x / _Resolution.y; - - int samples = _SamplesPerPixel; - int bounces = _Bounces; - float3 result = float3(0, 0, 0); - - for (int i = 0; i < samples; i++) - { - // get a ray for the uv - Ray ray = createCameraRay(uv); - - // trace and shade - for (int i = 0; i < bounces; i++) - { - RayHit hit = trace(ray); - result += ray.energy * shade(ray, hit); - - if (!any(ray.energy)) break; - } - } - - result /= (float)samples; - - Result[id.xy] = float4(result, 1); -} diff --git a/res/tex.png b/res/tex.png deleted file mode 100644 index b983971..0000000 --- a/res/tex.png +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:94a4eea9b39f31fa150c4ce82786b0fb7428e4b61257581c458c3ef01956f8a7 -size 1115422 diff --git a/src/_shader/root/rt.compute b/src/_shader/root/rt.compute deleted file mode 100644 index 3974d70..0000000 --- a/src/_shader/root/rt.compute +++ /dev/null @@ -1,506 +0,0 @@ -// Each #kernel tells which function to compile; you can have many kernels -#pragma kernel CSMain - -struct Sphere -{ - float3 position; - float radius; - float3 albedo; - float3 specular; - float3 emission; -}; - -struct Tube -{ - float3 position; - float3 axis; - float radius; - float height; - float thickness; -}; - -struct Unit -{ - float3 position; - int team; - int selected; -}; - -// Create a RenderTexture with enableRandomWrite flag and set it -// with cs.SetTexture -RWTexture2D Result; -float2 _Pixel; -float _Seed; -float _EmissionScale; -int _Bounces; -int _SamplesPerPixel; - -// camera -float2 _Resolution; -float4x4 _CameraToWorld; -float4x4 _CameraInverseProjection; -float3 _CameraW; -float3 _CameraU; -float3 _CameraV; -float3 _CameraHorizontal; -float3 _CameraVertical; -float3 _CameraLowerLeftCorner; -float _CameraLensRadius; -float _CameraFocusDistance; - -// environment -float _GroundHeight; -float3 _GroundColor; -float _SkyHeight; -float _SkyHoleRadius; -float3 _SkyColor; - -int _ActiveSpheres; -int _ActiveTubes; - -int _ActiveUnits; -float3 _UnitColor; -float _UnitRadius; - -StructuredBuffer _Units; -StructuredBuffer _Tubes; -StructuredBuffer _Spheres; - -#define GROUP_SIZE 32 - -static const float PI = 3.14159265f; -static const float BIG = 1000000.0f; // not infinity but close enough - -static const int MAT_LAMBERT = 0; -static const int MAT_DIELECTRIC = 1; - -struct Ray -{ - float3 origin; - float3 direction; - float3 energy; -}; - -Ray createRay(float3 origin, float3 direction) -{ - Ray ray; - ray.origin = origin; - ray.direction = direction; - ray.energy = float3(1.0f, 1.0f, 1.0f); - return ray; -} - -struct RayHit -{ - float3 position; - float distance; - float3 normal; - float3 albedo; - float3 specular; - float3 emission; -}; - -RayHit createRayHit() -{ - RayHit hit; - hit.position = float3(0.0f, 0.0f, 0.0f); - hit.distance = BIG; - hit.normal = float3(0.0f, 0.0f, 0.0f); - hit.albedo = float3(0.0f, 0.0f, 0.0f); - hit.specular = float3(0.0f, 0.0f, 0.0f); - hit.emission = float3(0.0f, 0.0f, 0.0f); - return hit; -} - -float rand() -{ - float result = frac(sin(_Seed / 100.0f * dot(_Pixel, float2(12.9898f, 78.233f))) * 43758.5453f); - _Seed += 1.0f; - return result; -} - -float sdot(float3 x, float3 y, float f = 1.0f) -{ - return saturate(dot(x, y) * f); -} - -float3x3 getTangentSpace(float3 normal) -{ - // helper vector for the cross product - float3 helper = float3(1, 0, 0); - if (abs(normal.x) > 0.99f) - { - helper = float3(0, 0, 1); - } - - // generate vectors - float3 tangent = normalize(cross(normal, helper)); - float3 binormal = normalize(cross(normal, tangent)); - return float3x3(tangent, binormal, normal); -} - -float3 sampleHemisphere(float3 normal) -{ - // uniformly sample hemisphere direction - float cosTheta = rand(); - float sinTheta = sqrt(max(0.0f, 1.0f - cosTheta * cosTheta)); - float phi = 2 * PI * rand(); - float3 tangentSpaceDir = float3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta); - - // transform direction to world space - return mul(tangentSpaceDir, getTangentSpace(normal)); -} - -float2 randomInUnitDisk() -{ - // pick a random radius and angle then convert to cartesian - float r = rand(); - float theta = rand() * 2 * PI; - return float2(cos(theta), sin(theta)) * r; -} - -Ray createCameraRay(float2 uv) -{ - // transform -1..1 -> 0..1 - uv = uv * 0.5 + 0.5; - uv.x = 1 - uv.x; - - // transform the camera origin to world space - float3 origin = mul(_CameraToWorld, float4(0.0f, 0.0f, 0.0f, 1.0f)).xyz; - - // offset from centre of the lens for depth of field - float2 rd = _CameraLensRadius * randomInUnitDisk(); - float3 offset = _CameraU * rd.x + _CameraV * rd.y; - - origin += offset; - - // invert the perspective projection of the view-space position - float3 direction = mul(_CameraInverseProjection, float4(uv, 0.0f, 1.0f)).xyz; - - // transform the direction from camera to world space and normalize - direction = mul(_CameraToWorld, float4(direction, 0.0f)).xyz; - - direction = _CameraLowerLeftConer - + uv.x * _CameraHorizontal - + uv.y * _CameraVertical - - origin; - - // direction = mul(_CameraInverseProjection, float4(direction, 0)).xyz; - - direction = normalize(direction); - return createRay(origin, direction); -} - - -void intersectSphere(Ray ray, inout RayHit bestHit, Sphere sphere) -{ - // calculate distance along the ray where the sphere is intersected - float3 d = ray.origin - sphere.position; - float p1 = -dot(ray.direction, d); - float p2sqr = p1 * p1 - dot(d, d) + sphere.radius * sphere.radius; - - 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 - sphere.position); - bestHit.albedo = sphere.albedo; - bestHit.specular = sphere.specular; - bestHit.emission = sphere.emission; - } -} - -float intersectPlane(Ray ray, float3 p, float3 normal) -{ - float denom = dot(normal, ray.direction); - - if (abs(denom) > 0.0001) - { - float t = dot(p - ray.origin, normal) / denom; - if (t >= 0) return t; - } - return -1; -} - -// https://www.iquilezles.org/www/articles/intersectors/intersectors.htm -// cylinder defined in extremes pa and pb, and radius ra -float4 intersectCylinder(Ray ray, float3 pa, float3 pb, float ra, bool inner) -{ - float3 ro = ray.origin; - float3 rd = ray.direction; - - // central axis - float3 ca = pb - pa; - // eye to base - float3 oc = ro - pa; - - // dot products - float caca = dot(ca, ca); - float card = dot(ca, rd); - float caoc = dot(ca, oc); - - // find intersects - float a = caca - card * card; - float b = caca * dot(oc, rd) - caoc * card; - float c = caca * dot(oc, oc) - caoc * caoc - ra * ra * caca; - float h = b * b - a * c; - - if (h < 0.0) return float4(-1, 0, 0, 0); // no intersection - - h = sqrt(h); - h = inner?-h:h; - float t = (-b - h) / a; - - // body - float y = caoc + t * card; - if (y > 0.0 && y < caca) return float4(t, (oc+t*rd - ca*y/caca) / ra); - - // caps - t = ((y < 0.0 ? 0.0 : caca) - caoc) / card; - if (abs(b + a * t) < h) return float4(t, ca * sign(y) / caca); - - return float4(-1, 0, 0, 0); // no intersection -} - -float sdSegment(float3 p, float3 a, float3 b, float r) -{ - float3 pa = p - a, ba = b - a; - float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0); - return length(pa - ba * h) - r; -} - -float opSubtraction(float d1, float d2) { return max(-d1, d2); } - -void intersectTube(Ray ray, inout RayHit bestHit, Tube tube) -{ - // TODO: inner tube - - float height = tube.height; - - float3 axis = normalize(tube.axis); - - float3 pa = tube.position + axis * -height * 0.5; - float3 pb = tube.position + axis * height * 0.5; - - float r_inner = (tube.radius-tube.thickness)/tube.radius; - - // where the ray hit the outer surface - float4 outerHit = intersectCylinder(ray, pa, pb, tube.radius, false); - // outerHit = float4(-1,0,0,0); - // where we hit the inner surface - float4 innerHit = intersectCylinder(ray, pa, pb, tube.radius * r_inner, true); - // float4 innerHit = float4(-1,0,0,0); - // innerHit.yzw *= -1; - - if (outerHit.x < 0 && innerHit.x < 0) return; - - float3 pos_outer = ray.origin + outerHit.x * ray.direction; - float axis_distance = sdSegment(pos_outer, pa, pb, 0); - - float t = bestHit.distance; - - // hit the inner surface - if (innerHit.x > 0 && innerHit.x < bestHit.distance) - { - t = innerHit.x; - - bestHit.normal = normalize(innerHit.yzw); - bestHit.position = ray.origin + t * ray.direction; - bestHit.distance = t; - - bestHit.albedo = float3(.5, .5, .5); - bestHit.emission = float3(0, 0, 0); - bestHit.specular = float3(1, 1, 1); - } - - // hit the outer surface - if (outerHit.x > 0 && outerHit.x < bestHit.distance && axis_distance > tube.radius*r_inner) - { - t = outerHit.x; - - bestHit.normal = normalize(outerHit.yzw); - bestHit.position = ray.origin + t * ray.direction; - bestHit.distance = t; - - bestHit.albedo = float3(.5, .5, .5); - bestHit.emission = float3(0, 0, 0); - bestHit.specular = float3(1, 1, 1); - } -} - -void intersectGroundPlane(inout Ray ray, inout RayHit bestHit) -{ - float3 albedo = _GroundColor; - float3 specular = float3(0, 0, 0); - - // calculate distance along the ray where the ground plane is intersected - float t = -(ray.origin.y - _GroundHeight) / ray.direction.y; - - if (t > 0 && t < bestHit.distance) - { - bestHit.distance = t; - bestHit.position = ray.origin + t * ray.direction; - bestHit.normal = float3(0.0f, 1.0f, 0.0f); - bestHit.albedo = albedo; - bestHit.specular = specular; - bestHit.emission = float3(0, 0, 0); - } -} - -void intersectCeilingPlane(inout Ray ray, inout RayHit bestHit) -{ - float albedo = _SkyColor; - float3 specular = float3(0, 0, 0); - - // ignore plane if the ray is coming from above - if (ray.direction.y < 0) return; - - float t = -(ray.origin.y - _SkyHeight) / ray.direction.y; - float3 p = ray.origin + ray.direction * t; - - if (length(p.xz) < _SkyHoleRadius) return; - - if (t > 0 && t < bestHit.distance) - { - bestHit.distance = t; - bestHit.position = ray.origin + t * ray.direction; - bestHit.normal = float3(0.0f, -1.0f, 0.0f); - bestHit.albedo = albedo; - bestHit.specular = specular; - bestHit.emission = float3(0, 0, 0); - } -} - -void intersectWall(inout Ray ray, inout RayHit bestHit) -{ - // ignore collision if ray's angle is steep or negative - float a = dot(float3(0, 1, 0), ray.direction); - if (a > 0.2 || a < 0) return; - - Sphere sphere; - sphere.radius = BIG - 1; - sphere.albedo = float3(1, 1, 1) * 1.98; - sphere.specular = float3(0, 0, 0); - sphere.emission = float3(0, 0, 0); - sphere.position = float3(0, 0, 0); - - intersectSphere(ray, bestHit, sphere); -} - -RayHit trace(Ray ray) -{ - RayHit bestHit = createRayHit(); - - intersectWall(ray, bestHit); - intersectGroundPlane(ray, bestHit); - intersectCeilingPlane(ray, bestHit); - - uint numSpheres, numTubes, stride; - - // celestial bodies - // _Spheres.GetDimensions(_ActiveSpheres, stride); - for (uint i = 0; i < _ActiveSpheres; i++) - { - intersectSphere(ray, bestHit, _Spheres[i]); - } - - // _Tubes.GetDimensions(numTubes, stride); - for (uint i = 0; i < _ActiveTubes; i++) - { - intersectTube(ray, bestHit, _Tubes[i]); - } - - if (_ActiveUnits > 0) - { - // units - _Units.GetDimensions(numSpheres, stride); - for (uint i = 0; i < _ActiveUnits; i++) - { - Unit unit = _Units[i]; - - float3 color = float3 - (lerp(1, 0, unit.team), - 0, - lerp(0, 1, unit.team)); - - Sphere s; - s.albedo = color; - s.emission = color * unit.selected; - s.specular = float3(0, 0, 0); - s.radius = _UnitRadius; - s.position = unit.position; - - intersectSphere(ray, bestHit, s); - } - } - - return bestHit; -} - -float3 scatter_lambert(inout Ray ray, RayHit hit) -{ - ray.origin = hit.position + hit.normal * 0.001f; - ray.direction = sampleHemisphere(hit.normal); - ray.energy *= 2 * hit.albedo * sdot(hit.normal, ray.direction); - return 0.0f; -} - -float3 shade(inout Ray ray, RayHit hit) -{ - if (any(hit.emission)) return hit.emission; - - if (hit.distance < BIG) - { - return scatter_lambert(ray, hit) + hit.emission; - } - else - { - ray.energy = 0.0f; - - // float theta = acos(ray.direction.y) / -PI; - // float phi = atan2(ray.direction.x, -ray.direction.z) / -PI * 0.5f; - - return _SkyColor; - } -} - -[numthreads(GROUP_SIZE,GROUP_SIZE,1)] -void CSMain(uint3 id : SV_DispatchThreadID) -{ - _Pixel = id.xy; - - // get dimensions of render texture - uint width, height; - Result.GetDimensions(width, height); - - // transform pixel to -1, 1 range - float2 uv = float2(id.xy / float2(width, height) * 2.0f - 1.0f); - uv.x *= _Resolution.x / _Resolution.y; - - int samples = _SamplesPerPixel; - int bounces = _Bounces; - float3 result = float3(0, 0, 0); - - for (int i = 0; i < samples; i++) - { - // get a ray for the uv - Ray ray = createCameraRay(uv); - - // trace and shade - for (int i = 0; i < bounces; i++) - { - RayHit hit = trace(ray); - result += ray.energy * shade(ray, hit); - - if (!any(ray.energy)) break; - } - } - - result /= (float)samples; - - Result[id.xy] = float4(result, 1); -} diff --git a/src/_shader/root/shader.frag b/src/_shader/root/shader.frag deleted file mode 100644 index cadfb1e..0000000 --- a/src/_shader/root/shader.frag +++ /dev/null @@ -1,12 +0,0 @@ -#version 430 core -out vec4 FragColor; - -in vec3 ourColor; -in vec2 TexCoord; - -uniform sampler2D ourTexture; - -void main() -{ - FragColor = texture(ourTexture, TexCoord); -} diff --git a/src/_shader/root/shader.vert b/src/_shader/root/shader.vert deleted file mode 100644 index 0e349b5..0000000 --- a/src/_shader/root/shader.vert +++ /dev/null @@ -1,14 +0,0 @@ -#version 430 core -layout (location = 0) in vec3 aPos; // position has attribute position 0 -layout (location = 1) in vec3 aColor; // color has attribute position 1 -layout (location = 2) in vec2 aTexCoord; // texture coordinate - -out vec3 ourColor; -out vec2 TexCoord; - -void main() -{ - gl_Position = vec4(aPos, 1.0); - ourColor = aColor; - TexCoord = aTexCoord; -} diff --git a/src/main.c b/src/main.c index 9ede8bc..894738b 100644 --- a/src/main.c +++ b/src/main.c @@ -13,7 +13,6 @@ int main() { int width = 420; int height = 420; - const char* texPath = "res/tex.png"; // create a window and opengl context SDL_Window* window = gfxInit(width, height); @@ -23,10 +22,11 @@ int main() printWorkGroupLimits(); // compile shader programs - unsigned int computeProgram = compileComputeShaderProgram("res/shader/compute.glsl"); + unsigned int computeProgram = compileComputeShaderProgram( + "bin/res/compute.compute"); unsigned int quadProgram = compileQuadShaderProgram( - "res/shader/shader.vert", - "res/shader/shader.frag"); + "bin/res/shader.vert", + "bin/res/shader.frag"); // initialise quad initBuffers(); @@ -51,8 +51,8 @@ int main() // normal drawing pass glUseProgram(quadProgram); - glClearColor(0.2f, 0.3f, 0.3f, 1.0f); - glClear(GL_COLOR_BUFFER_BIT); + //glClearColor(0.2f, 0.3f, 0.3f, 1.0f); + //glClear(GL_COLOR_BUFFER_BIT); glActiveTexture(GL_TEXTURE0); // use computed texture glBindTexture(GL_TEXTURE_2D, textureOutput); diff --git a/todo.md b/todo.md index 8ab5c4a..10d1098 100644 --- a/todo.md +++ b/todo.md @@ -1,7 +1,11 @@ * [x] basic opengl initialisation * [-] shader pre-processor * [x] ppp.py - * [ ] shader src and out + * [ ] read root shaders from src/shader/ + * [ ] read include shaders from src shader/ include + * [ ] write processed shaders to bin/res/shader/ + * [ ] attempt to compile processed shaders + * [ ] output frame to a file * [ ] detect input keydown s * [ ] get timestamp