preprocess shaders
This commit is contained in:
parent
134e264bcd
commit
8d0937cb50
40
makefile
40
makefile
|
@ -2,11 +2,22 @@ SRC_DIR = src
|
||||||
BIN_DIR = bin
|
BIN_DIR = bin
|
||||||
RES_DIR = res
|
RES_DIR = res
|
||||||
|
|
||||||
SHADER_DIR = $(SRC_DIR)/_shader
|
SHADER_DIR = $(RES_DIR)/shader
|
||||||
SHADER_ROOT_DIR = $(SHADER_DIR)/root
|
SHADER_QUAD_DIR = $(SHADER_DIR)/quad
|
||||||
SHADER_INCLUDE_DIR = $(SHADER_DIR)/common
|
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
|
TARGET = $(BIN_DIR)/oglc
|
||||||
|
|
||||||
CC = gcc
|
CC = gcc
|
||||||
LIBS = `pkg-config --static --libs glew sdl2`
|
LIBS = `pkg-config --static --libs glew sdl2`
|
||||||
CFLAGS = -I$(SRC_DIR) -Wall
|
CFLAGS = -I$(SRC_DIR) -Wall
|
||||||
|
@ -14,24 +25,20 @@ CFLAGS = -I$(SRC_DIR) -Wall
|
||||||
SRC = $(shell find $(SRC_DIR) -name *.c)
|
SRC = $(shell find $(SRC_DIR) -name *.c)
|
||||||
OBJ = $(SRC:%.c=%.o)
|
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
|
# create dirs if they dont exist
|
||||||
_dummy := $(shell mkdir -p $(BIN_DIR))
|
_dummy := $(shell mkdir -p $(BIN_DIR))
|
||||||
|
|
||||||
$(TARGET): $(OBJ)
|
$(TARGET): $(OBJ) $(SHADER_TARGETS)
|
||||||
# preprocess shaders and store results in bin/res/shader/ under root name
|
mkdir -p $(BIN_DIR)
|
||||||
foreach root,$(SHADER_ROOT_DIR),$(echo $(root))
|
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)
|
$(SHADER_TARGET_DIR)/%.compute: $(SHADER_ROOT_DIR)/%.glsl $(SHADER_INCLUDES)
|
||||||
cp -r $(RES_DIR) $(BIN_DIR)
|
mkdir -p $(SHADER_TARGET_DIR)
|
||||||
|
python ppp.py $< $(SHADER_INCLUDES) > $@
|
||||||
|
|
||||||
|
# how to make a .o out of a .c
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(CC) $(CFLAGS) -c -o $@ $<
|
$(CC) $(CFLAGS) -c -o $@ $<
|
||||||
|
|
||||||
|
@ -42,5 +49,4 @@ clean:
|
||||||
run: $(TARGET)
|
run: $(TARGET)
|
||||||
$(TARGET)
|
$(TARGET)
|
||||||
|
|
||||||
|
|
||||||
.PHONY: run clean
|
.PHONY: run clean
|
||||||
|
|
|
@ -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)
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:126baccff187648acfad78c57560035f5c783cc6ec92a37a93a5b1edc97af10e
|
|
||||||
size 184939
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
struct Sphere
|
||||||
|
{
|
||||||
|
vec3 center;
|
||||||
|
float radius;
|
||||||
|
vec3 albedo;
|
||||||
|
vec3 specular;
|
||||||
|
vec3 emission;
|
||||||
|
};
|
|
@ -7,11 +7,7 @@ layout(rgba32f, binding = 0) uniform image2D img_output; // rgba32f defines i
|
||||||
|
|
||||||
const float INF = 1000000.0f;
|
const float INF = 1000000.0f;
|
||||||
|
|
||||||
struct Sphere
|
#include sphere.glsl
|
||||||
{
|
|
||||||
vec3 center;
|
|
||||||
float radius;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Ray
|
struct Ray
|
||||||
{
|
{
|
||||||
|
@ -47,7 +43,7 @@ void intersectSphere(Ray ray, inout RayHit bestHit, Sphere sphere)
|
||||||
Ray createCameraRay(vec2 uv)
|
Ray createCameraRay(vec2 uv)
|
||||||
{
|
{
|
||||||
// transform -1..1 -> 0..1
|
// transform -1..1 -> 0..1
|
||||||
uv = uv*0.5+0.5;
|
//uv = uv*0.5+0.5;
|
||||||
//uv.x=1-uv.x;
|
//uv.x=1-uv.x;
|
||||||
|
|
||||||
// transform camera origin to world space
|
// transform camera origin to world space
|
|
@ -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<float4> 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<Unit> _Units;
|
|
||||||
StructuredBuffer<Tube> _Tubes;
|
|
||||||
StructuredBuffer<Sphere> _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);
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:94a4eea9b39f31fa150c4ce82786b0fb7428e4b61257581c458c3ef01956f8a7
|
|
||||||
size 1115422
|
|
|
@ -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<float4> 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<Unit> _Units;
|
|
||||||
StructuredBuffer<Tube> _Tubes;
|
|
||||||
StructuredBuffer<Sphere> _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);
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
}
|
|
12
src/main.c
12
src/main.c
|
@ -13,7 +13,6 @@ int main()
|
||||||
{
|
{
|
||||||
int width = 420;
|
int width = 420;
|
||||||
int height = 420;
|
int height = 420;
|
||||||
const char* texPath = "res/tex.png";
|
|
||||||
|
|
||||||
// create a window and opengl context
|
// create a window and opengl context
|
||||||
SDL_Window* window = gfxInit(width, height);
|
SDL_Window* window = gfxInit(width, height);
|
||||||
|
@ -23,10 +22,11 @@ int main()
|
||||||
printWorkGroupLimits();
|
printWorkGroupLimits();
|
||||||
|
|
||||||
// compile shader programs
|
// compile shader programs
|
||||||
unsigned int computeProgram = compileComputeShaderProgram("res/shader/compute.glsl");
|
unsigned int computeProgram = compileComputeShaderProgram(
|
||||||
|
"bin/res/compute.compute");
|
||||||
unsigned int quadProgram = compileQuadShaderProgram(
|
unsigned int quadProgram = compileQuadShaderProgram(
|
||||||
"res/shader/shader.vert",
|
"bin/res/shader.vert",
|
||||||
"res/shader/shader.frag");
|
"bin/res/shader.frag");
|
||||||
|
|
||||||
// initialise quad
|
// initialise quad
|
||||||
initBuffers();
|
initBuffers();
|
||||||
|
@ -51,8 +51,8 @@ int main()
|
||||||
|
|
||||||
// normal drawing pass
|
// normal drawing pass
|
||||||
glUseProgram(quadProgram);
|
glUseProgram(quadProgram);
|
||||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
//glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
//glClear(GL_COLOR_BUFFER_BIT);
|
||||||
glActiveTexture(GL_TEXTURE0); // use computed texture
|
glActiveTexture(GL_TEXTURE0); // use computed texture
|
||||||
glBindTexture(GL_TEXTURE_2D, textureOutput);
|
glBindTexture(GL_TEXTURE_2D, textureOutput);
|
||||||
|
|
||||||
|
|
6
todo.md
6
todo.md
|
@ -1,7 +1,11 @@
|
||||||
* [x] basic opengl initialisation
|
* [x] basic opengl initialisation
|
||||||
* [-] shader pre-processor
|
* [-] shader pre-processor
|
||||||
* [x] ppp.py
|
* [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
|
* [ ] output frame to a file
|
||||||
* [ ] detect input keydown s
|
* [ ] detect input keydown s
|
||||||
* [ ] get timestamp
|
* [ ] get timestamp
|
||||||
|
|
Loading…
Reference in New Issue