Basic path-trace renderer pipeline.

This commit is contained in:
Lubos Lenco 2016-03-17 19:29:53 +01:00
parent ed6f725944
commit bbd7ee2353
10 changed files with 1148 additions and 10 deletions

View file

@ -0,0 +1,171 @@
package cycles.trait;
import lue.Eg;
import lue.math.Mat4;
import lue.math.Vec4;
import lue.trait.Trait;
import lue.node.Transform;
import lue.node.RootNode;
import lue.resource.MaterialResource.MaterialContext;
class PathTracer extends Trait {
var context:MaterialContext;
var eyeLocation:Int;
var lightLocation:Int;
var ray00Location:Int;
var ray01Location:Int;
var ray10Location:Int;
var ray11Location:Int;
var objectLocations:Array<Int>;
var transformMap:Map<Int, Transform>;
public function new() {
super();
requestInit(init);
requestUpdate(update);
}
function init() {
context = Eg.getMaterialResource('pt_material').getContext('pt_trace_pass');
context.resource.bind_constants.push(
{
id: "eye",
vec3: [0.0, 0.0, 0.0]
}
);
eyeLocation = context.resource.bind_constants.length - 1;
context.resource.bind_constants.push(
{
id: "light",
vec3: [0.9, 0.6, 1.1]
}
);
lightLocation = context.resource.bind_constants.length - 1;
context.resource.bind_constants.push(
{
id: "glossiness",
float: 0.6
}
);
context.resource.bind_constants.push(
{
id: "ray00",
vec3: [0.0, 0.0, 0.0]
}
);
ray00Location = context.resource.bind_constants.length - 1;
context.resource.bind_constants.push(
{
id: "ray01",
vec3: [0.0, 0.0, 0.0]
}
);
ray01Location = context.resource.bind_constants.length - 1;
context.resource.bind_constants.push(
{
id: "ray10",
vec3: [0.0, 0.0, 0.0]
}
);
ray10Location = context.resource.bind_constants.length - 1;
context.resource.bind_constants.push(
{
id: "ray11",
vec3: [0.0, 0.0, 0.0]
}
);
ray11Location = context.resource.bind_constants.length - 1;
objectLocations = [];
transformMap = new Map();
var sphereNum = 0;
for (n in RootNode.models) {
if (n.id.split(".")[0] == "Sphere") {
context.resource.bind_constants.push(
{
id: "sphereCenter" + sphereNum,
vec3: [0.0, 0.0, 0.0]
}
);
var loc = context.resource.bind_constants.length - 1;
objectLocations.push(loc);
transformMap.set(loc, n.transform);
context.resource.bind_constants.push(
{
id: "sphereRadius" + sphereNum,
float: 0.288
}
);
sphereNum++;
}
}
}
function update() {
var camera = RootNode.cameras[0];
var eye = camera.transform.pos;
//var jitter = Mat4.identity();
//jitter.initTranslate(Math.random() * 2 - 1, Math.random() * 2 - 1, 0);
//jitter.multiplyScalar(1 / lue.App.w);
var mvp = Mat4.identity();
mvp.mult2(camera.V);
mvp.mult2(camera.P);
var inverse = Mat4.identity();
// jitter.mult2(mvp);
inverse.inverse2(mvp);
var matrix = inverse;
// Set uniforms
context.resource.bind_constants[eyeLocation].vec3 = [eye.x, eye.y, eye.z];
var v = getEyeRay(matrix, -1, -1, eye);
context.resource.bind_constants[ray00Location].vec3 = [v.x, v.y, v.z];
var v = getEyeRay(matrix, -1, 1, eye);
context.resource.bind_constants[ray01Location].vec3 = [v.x, v.y, v.z];
var v = getEyeRay(matrix, 1, -1, eye);
context.resource.bind_constants[ray10Location].vec3 = [v.x, v.y, v.z];
var v = getEyeRay(matrix, 1, 1, eye);
context.resource.bind_constants[ray11Location].vec3 = [v.x, v.y, v.z];
for (loc in objectLocations) {
var t:Transform = transformMap.get(loc);
context.resource.bind_constants[loc].vec3[0] = t.absx();
context.resource.bind_constants[loc].vec3[1] = t.absy();
context.resource.bind_constants[loc].vec3[2] = t.absz();
}
}
function getEyeRay(matrix:Mat4, x:Float, y:Float, eye:Vec4):Vec4 {
var v = new Vec4();
var vv = new kha.math.FastVector4(x, y, 0, 1);
vv = matrix.multvec(vv);
v.x = vv.x;
v.y = vv.y;
v.z = vv.z;
v.w = vv.w;
v.x /= v.w;
v.y /= v.w;
v.z /= v.w;
v.w /= v.w;
v.sub(eye);
return v;
}
}

418
blender/path_tracer.py Normal file
View file

@ -0,0 +1,418 @@
# Based on WebGL Path Tracing by Evan Wallace
# (http://madebyevan.com/webgl-path-tracing/)
import bpy
import os
bounces = '5'
epsilon = '0.0001'
infinity = '10000.0'
lightSize = 0.1
lightVal = 0.5
objects = None
sampleCount = 0
MATERIAL_DIFFUSE = 0
MATERIAL_MIRROR = 1
MATERIAL_GLOSSY = 2
material = MATERIAL_MIRROR
YELLOW_BLUE_CORNELL_BOX = 0
RED_GREEN_CORNELL_BOX = 1
environment = YELLOW_BLUE_CORNELL_BOX
def concat(objects, func):
text = ''
for i in range(0, len(objects)):
text += func(objects[i])
return text
tracerFragmentSourceHeader = """
#version 450
#ifdef GL_ES
precision mediump float;
#endif
in vec3 initialRay;
in vec2 texCoord;
uniform vec3 eye;
//uniform float textureWeight;
uniform float timeSinceStart;
//uniform sampler2D stexture;
uniform float glossiness;
vec3 roomCubeMin = vec3(-1.0, -1.0, -1.0);
vec3 roomCubeMax = vec3(1.0, 1.0, 1.0);
"""
# compute the near and far intersections of the cube (stored in the x and y components) using the slab method
# no intersection means vec.x > vec.y (really tNear > tFar)
intersectCubeSource = """
vec2 intersectCube(vec3 origin, vec3 ray, vec3 cubeMin, vec3 cubeMax) {
vec3 tMin = (cubeMin - origin) / ray;
vec3 tMax = (cubeMax - origin) / ray;
vec3 t1 = min(tMin, tMax);
vec3 t2 = max(tMin, tMax);
float tNear = max(max(t1.x, t1.y), t1.z);
float tFar = min(min(t2.x, t2.y), t2.z);
return vec2(tNear, tFar);
}
"""
# given that hit is a point on the cube, what is the surface normal?
# TODO: do this with fewer branches
normalForCubeSource = """
vec3 normalForCube(vec3 hit, vec3 cubeMin, vec3 cubeMax) {
if (hit.x < cubeMin.x + """ + epsilon + """) return vec3(-1.0, 0.0, 0.0);
else if (hit.x > cubeMax.x - """ + epsilon + """) return vec3(1.0, 0.0, 0.0);
else if (hit.y < cubeMin.y + """ + epsilon + """) return vec3(0.0, -1.0, 0.0);
else if (hit.y > cubeMax.y - """ + epsilon + """) return vec3(0.0, 1.0, 0.0);
else if (hit.z < cubeMin.z + """ + epsilon + """) return vec3(0.0, 0.0, -1.0);
//else return vec3(0.0, 0.0, 1.0);
return vec3(0.0, 0.0, 1.0);
}
"""
# compute the near intersection of a sphere
# no intersection returns a value of +infinity
intersectSphereSource = """
float intersectSphere(vec3 origin, vec3 ray, vec3 sphereCenter, float sphereRadius) {
vec3 toSphere = origin - sphereCenter;
float a = dot(ray, ray);
float b = 2.0 * dot(toSphere, ray);
float c = dot(toSphere, toSphere) - sphereRadius*sphereRadius;
float discriminant = b*b - 4.0*a*c;
if (discriminant > 0.0) {
float t = (-b - sqrt(discriminant)) / (2.0 * a);
if (t > 0.0) return t;
}
return """ + infinity + """;
}
"""
# given that hit is a point on the sphere, what is the surface normal?
normalForSphereSource = """
vec3 normalForSphere(vec3 hit, vec3 sphereCenter, float sphereRadius) {
return (hit - sphereCenter) / sphereRadius;
}
"""
# random cosine-weighted distributed vector
# from http://www.rorydriscoll.com/2009/01/07/better-sampling/
cosineWeightedDirectionSource = """
vec3 cosineWeightedDirection(float seed, vec3 normal) {
float u = random(vec3(12.9898, 78.233, 151.7182), seed);
float v = random(vec3(63.7264, 10.873, 623.6736), seed);
float r = sqrt(u);
float angle = 6.283185307179586 * v;
// compute basis from normal
vec3 sdir, tdir;
if (abs(normal.x) < 0.5) {
sdir = cross(normal, vec3(1.0, 0.0, 0.0));
}
else {
sdir = cross(normal, vec3(0.0, 1.0, 0.0));
}
tdir = cross(normal, sdir);
return r*cos(angle)*sdir + r*sin(angle)*tdir + sqrt(1.0-u)*normal;
}
"""
# use the fragment position for randomness
randomSource = """
float random(vec3 scale, float seed) {
return fract(sin(dot(texCoord.xyx + seed, scale)) * 43758.5453 + seed);
// return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);
}
"""
# random normalized vector
uniformlyRandomDirectionSource = """
vec3 uniformlyRandomDirection(float seed) {
float u = random(vec3(12.9898, 78.233, 151.7182), seed);
float v = random(vec3(63.7264, 10.873, 623.6736), seed);
float z = 1.0 - 2.0 * u;
float r = sqrt(1.0 - z * z);
float angle = 6.283185307179586 * v;
return vec3(r * cos(angle), r * sin(angle), z);
}
"""
# random vector in the unit sphere
# note: this is probably not statistically uniform, saw raising to 1/3 power somewhere but that looks wrong?
uniformlyRandomVectorSource = """
vec3 uniformlyRandomVector(float seed) {
return uniformlyRandomDirection(seed) * sqrt(random(vec3(36.7539, 50.3658, 306.2759), seed));
}
"""
# compute specular lighting contribution
specularReflection = """
vec3 reflectedLight = normalize(reflect(light - hit, normal));
specularHighlight = max(0.0, dot(reflectedLight, normalize(hit - origin)));"""
# update ray using normal and bounce according to a diffuse reflection
newDiffuseRay = """
ray = cosineWeightedDirection(time + float(bounce), normal);"""
# update ray using normal according to a specular reflection
newReflectiveRay = """
ray = reflect(ray, normal);
""" + specularReflection + """
specularHighlight = 2.0 * pow(specularHighlight, 20.0);"""
# update ray using normal and bounce according to a glossy reflection
newGlossyRay = """
ray = normalize(reflect(ray, normal)) + uniformlyRandomVector(time + float(bounce)) * glossiness;
""" + specularReflection + """
specularHighlight = pow(specularHighlight, 3.0);"""
yellowBlueCornellBox = """
if (hit.x < -0.9999) surfaceColor = vec3(0.1, 0.5, 1.0); // blue
else if (hit.x > 0.9999) surfaceColor = vec3(1.0, 0.9, 0.1); // yellow"""
redGreenCornellBox = """
if (hit.x < -0.9999) surfaceColor = vec3(1.0, 0.3, 0.1); // red
else if (hit.x > 0.9999) surfaceColor = vec3(0.3, 1.0, 0.1); // green"""
def _getShadowTestCode(o):
return o.getShadowTestCode()
def makeShadow(objects):
return """
float shadow(vec3 origin, vec3 ray) {
""" + concat(objects, _getShadowTestCode) + """
return 1.0;
}"""
def _getIntersectCode(o):
return o.getIntersectCode()
def _getMinimumIntersectCode(o):
return o.getMinimumIntersectCode()
def _getNormalCalculationCode(o):
return o.getNormalCalculationCode()
def makeCalculateColor(objects):
return """
vec3 calculateColor(float time, vec3 origin, vec3 ray, vec3 light) {
vec3 colorMask = vec3(1.0);
vec3 accumulatedColor = vec3(0.0);
// main raytracing loop
for (int bounce = 0; bounce < """ + bounces + """; bounce++) {
// compute the intersection with everything
vec2 tRoom = intersectCube(origin, ray, roomCubeMin, roomCubeMax);
""" + concat(objects, _getIntersectCode) + """
// find the closest intersection
float t = """ + infinity + """;
if (tRoom.x < tRoom.y) t = tRoom.y;
""" + concat(objects, _getMinimumIntersectCode) + """
// info about hit
vec3 hit = origin + ray * t;
vec3 surfaceColor = vec3(0.75);
float specularHighlight = 0.0;
vec3 normal;
// calculate the normal (and change wall color)
if (t == tRoom.y) {
normal = -normalForCube(hit, roomCubeMin, roomCubeMax);
""" + [yellowBlueCornellBox, redGreenCornellBox][environment] + """
""" + newDiffuseRay + """
}
else if (t == """ + infinity + """) {
break;
}
else {
int aa = 0;
if (aa == 1) {aa = 0;} // hack to discard the first 'else' in 'else if'
""" + concat(objects, _getNormalCalculationCode) + """
""" + [newDiffuseRay, newReflectiveRay, newGlossyRay][material] + """
}
// compute diffuse lighting contribution
vec3 toLight = light - hit;
float diffuse = max(0.0, dot(normalize(toLight), normal));
// trace a shadow ray to the light
float shadowIntensity = shadow(hit + normal * """ + epsilon + """, toLight);
// do light bounce
colorMask *= surfaceColor;
accumulatedColor += colorMask * (""" + str(lightVal) + """ * diffuse * shadowIntensity);
accumulatedColor += colorMask * specularHighlight * shadowIntensity;
// calculate next origin
origin = hit;
}
return accumulatedColor;
}
"""
def makeMain():
return """
void main() {
float time = timeSinceStart;
vec3 col = vec3(0.0);
const int samples = 1;
for (int i = 0; i < samples; i++) {
vec3 newLight = light + uniformlyRandomVector(time - 53.0) * """ + str(lightSize) + """;
col += calculateColor(time, eye, initialRay, newLight);
time += 0.35;
}
gl_FragColor = vec4(vec3(col / samples), 1.0);
}
"""
def _getGlobalCode(o):
return o.getGlobalCode()
def makeTracerFragmentSource(objects):
return tracerFragmentSourceHeader + \
concat(objects, _getGlobalCode) + \
intersectCubeSource + \
normalForCubeSource + \
intersectSphereSource + \
normalForSphereSource + \
randomSource + \
cosineWeightedDirectionSource + \
uniformlyRandomDirectionSource + \
uniformlyRandomVectorSource + \
makeShadow(objects) + \
makeCalculateColor(objects) + \
makeMain()
class Sphere:
def __init__(self, center, radius, id):
self.center = center;
self.radius = radius;
self.centerStr = 'sphereCenter' + str(id);
self.radiusStr = 'sphereRadius' + str(id);
self.intersectStr = 'tSphere' + str(id);
def getGlobalCode(self):
return """
uniform vec3 """ + self.centerStr + """;
uniform float """ + self.radiusStr + """;"""
def getIntersectCode(self):
return """
float """ + self.intersectStr + """ = intersectSphere(origin, ray, """ + self.centerStr + """, """ + self.radiusStr + """);"""
def getShadowTestCode(self):
return """
""" + self.getIntersectCode() + """
if (""" + self.intersectStr + """ < 1.0) return 0.0;"""
def getMinimumIntersectCode(self):
return """
if (""" + self.intersectStr + """ < t) t = """ + self.intersectStr + """;"""
def getNormalCalculationCode(self):
return """
else if (t == """ + self.intersectStr + """) normal = normalForSphere(hit, """ + self.centerStr + """, """ + self.radiusStr + """);"""
class Cube:
def __init__(self, minCorner, maxCorner, id):
self.minCorner = minCorner;
self.maxCorner = maxCorner;
self.minStr = 'cubeMin' + str(id);
self.maxStr = 'cubeMax' + str(id);
self.intersectStr = 'tCube' + str(id);
def getGlobalCode(self):
return """
uniform vec3 """ + self.minStr + """;
uniform vec3 """ + self.maxStr + """;"""
def getIntersectCode(self):
return """
vec2 """ + self.intersectStr + """ = intersectCube(origin, ray, """ + self.minStr + """, """ + self.maxStr + """);"""
def getShadowTestCode(self):
return """
""" + self.getIntersectCode() + """
if (""" + self.intersectStr + """.x > 0.0 && """ + self.intersectStr + """.x < 1.0 && """ + self.intersectStr + """.x < """ + self.intersectStr + """.y) return 0.0;"""
def getMinimumIntersectCode(self):
return """
if (""" + self.intersectStr + """.x > 0.0 && """ + self.intersectStr + """.x < """ + self.intersectStr + """.y && """ + self.intersectStr + """.x < t) t = """ + self.intersectStr + """.x;"""
def getNormalCalculationCode(self):
return """
// have to compare intersectStr.x < intersectStr.y otherwise two coplanar
// cubes will look wrong (one cube will "steal" the hit from the other)
else if (t == """ + self.intersectStr + """.x && """ + self.intersectStr + """.x < """ + self.intersectStr + """.y) normal = normalForCube(hit, """ + self.minStr + """, """ + self.maxStr + """);"""
class Light:
def __init__(self):
pass
def getGlobalCode(self):
return """uniform vec3 light;"""
def getIntersectCode(self):
return """"""
def getShadowTestCode(self):
return """"""
def getMinimumIntersectCode(self):
return """"""
def getNormalCalculationCode(self):
return """"""
def initObjects():
nextObjectId = 0
objects = []
objects.append(Light())
for o in bpy.context.scene.objects:
if o.name.split('.', 1)[0] == 'Sphere':
objects.append(Sphere([0, 0, 0], 0, nextObjectId))
nextObjectId += 1
return objects
objects = initObjects()
def compile(frag_path):
with open(frag_path, 'w') as f:
f.write(makeTracerFragmentSource(objects))

View file

@ -13,6 +13,7 @@ import write_data
import nodes_logic
import nodes_pipeline
import nodes_world
import path_tracer
from armory import ArmoryExporter
def defaultSettings():
@ -119,7 +120,8 @@ def exportGameData():
# TODO: cache
nodes_logic.buildNodeTrees()
nodes_pipeline.buildNodeTrees(shader_references, asset_references)
nodes_world.buildNodeTrees(shader_references, asset_references) # TODO: Have to build nodes everytime to collect env map resources, should be cached
# TODO: Have to build nodes everytime to collect env map resources, should be cached
nodes_world.buildNodeTrees(shader_references, asset_references)
# TODO: Set armatures to center of world so skin transform is zero
armatures = []
@ -188,6 +190,10 @@ def buildProject(self, build_type=0):
fp = os.path.sep.join(s)
os.chdir(fp)
# Compile path tracer shaders
if len(bpy.data.cameras) > 0 and bpy.data.cameras[0].pipeline_path == 'pathtrace_pipeline':
path_tracer.compile(raw_path + 'pt_trace_pass/pt_trace_pass.frag.glsl')
# Compile shaders if needed
if os.path.isdir("compiled") == False:
os.chdir(raw_path)

View file

@ -15,14 +15,22 @@ os.chdir('../env_map')
make_resources.make('env_map.shader.json')
make_variants.make('env_map.shader.json')
os.chdir('../ssao_pass')
make_resources.make('ssao_pass.shader.json')
make_variants.make('ssao_pass.shader.json')
# os.chdir('../ssao_pass')
# make_resources.make('ssao_pass.shader.json')
# make_variants.make('ssao_pass.shader.json')
os.chdir('../blur_pass')
make_resources.make('blur_pass.shader.json')
make_variants.make('blur_pass.shader.json')
# os.chdir('../blur_pass')
# make_resources.make('blur_pass.shader.json')
# make_variants.make('blur_pass.shader.json')
os.chdir('../combine_pass')
make_resources.make('combine_pass.shader.json')
make_variants.make('combine_pass.shader.json')
# os.chdir('../combine_pass')
# make_resources.make('combine_pass.shader.json')
# make_variants.make('combine_pass.shader.json')
os.chdir('../pt_trace_pass')
make_resources.make('pt_trace_pass.shader.json')
make_variants.make('pt_trace_pass.shader.json')
os.chdir('../pt_final_pass')
make_resources.make('pt_final_pass.shader.json')
make_variants.make('pt_final_pass.shader.json')

View file

@ -0,0 +1,14 @@
#version 450
#ifdef GL_ES
precision mediump float;
#endif
uniform sampler2D gbuffer;
in vec2 texCoord;
void main() {
vec3 col = texture(gbuffer, texCoord).rgb;
gl_FragColor = vec4(col, 1.0);
}

View file

@ -0,0 +1,33 @@
{
"contexts": [
{
"id": "pt_final_pass",
"params": [
{
"id": "depth_write",
"value": "false"
},
{
"id": "compare_mode",
"value": "always"
},
{
"id": "cull_mode",
"value": "none"
},
{
"id": "blend_source",
"value": "blend_one"
},
{
"id": "blend_destination",
"value": "blend_zero"
}
],
"links": [],
"texture_params": [],
"vertex_shader": "pt_final_pass.vert.glsl",
"fragment_shader": "pt_final_pass.frag.glsl"
}
]
}

View file

@ -0,0 +1,18 @@
#version 450
#ifdef GL_ES
precision highp float;
#endif
in vec2 pos;
out vec2 texCoord;
const vec2 madd = vec2(0.5, 0.5);
void main() {
// Scale vertex attribute to [0-1] range
texCoord = pos.xy * madd + madd;
gl_Position = vec4(pos.xy, 0.0, 1.0);
}

View file

@ -0,0 +1,408 @@
#version 450
#ifdef GL_ES
precision mediump float;
#endif
in vec3 initialRay;
in vec2 texCoord;
uniform vec3 eye;
//uniform float textureWeight;
uniform float timeSinceStart;
//uniform sampler2D stexture;
uniform float glossiness;
vec3 roomCubeMin = vec3(-1.0, -1.0, -1.0);
vec3 roomCubeMax = vec3(1.0, 1.0, 1.0);
uniform vec3 light;
uniform vec3 sphereCenter0;
uniform float sphereRadius0;
uniform vec3 sphereCenter1;
uniform float sphereRadius1;
uniform vec3 sphereCenter2;
uniform float sphereRadius2;
uniform vec3 sphereCenter3;
uniform float sphereRadius3;
uniform vec3 sphereCenter4;
uniform float sphereRadius4;
uniform vec3 sphereCenter5;
uniform float sphereRadius5;
uniform vec3 sphereCenter6;
uniform float sphereRadius6;
uniform vec3 sphereCenter7;
uniform float sphereRadius7;
uniform vec3 sphereCenter8;
uniform float sphereRadius8;
uniform vec3 sphereCenter9;
uniform float sphereRadius9;
uniform vec3 sphereCenter10;
uniform float sphereRadius10;
uniform vec3 sphereCenter11;
uniform float sphereRadius11;
uniform vec3 sphereCenter12;
uniform float sphereRadius12;
uniform vec3 sphereCenter13;
uniform float sphereRadius13;
uniform vec3 sphereCenter14;
uniform float sphereRadius14;
uniform vec3 sphereCenter15;
uniform float sphereRadius15;
uniform vec3 sphereCenter16;
uniform float sphereRadius16;
uniform vec3 sphereCenter17;
uniform float sphereRadius17;
uniform vec3 sphereCenter18;
uniform float sphereRadius18;
uniform vec3 sphereCenter19;
uniform float sphereRadius19;
uniform vec3 sphereCenter20;
uniform float sphereRadius20;
uniform vec3 sphereCenter21;
uniform float sphereRadius21;
uniform vec3 sphereCenter22;
uniform float sphereRadius22;
uniform vec3 sphereCenter23;
uniform float sphereRadius23;
uniform vec3 sphereCenter24;
uniform float sphereRadius24;
uniform vec3 sphereCenter25;
uniform float sphereRadius25;
uniform vec3 sphereCenter26;
uniform float sphereRadius26;
uniform vec3 sphereCenter27;
uniform float sphereRadius27;
uniform vec3 sphereCenter28;
uniform float sphereRadius28;
uniform vec3 sphereCenter29;
uniform float sphereRadius29;
vec2 intersectCube(vec3 origin, vec3 ray, vec3 cubeMin, vec3 cubeMax) {
vec3 tMin = (cubeMin - origin) / ray;
vec3 tMax = (cubeMax - origin) / ray;
vec3 t1 = min(tMin, tMax);
vec3 t2 = max(tMin, tMax);
float tNear = max(max(t1.x, t1.y), t1.z);
float tFar = min(min(t2.x, t2.y), t2.z);
return vec2(tNear, tFar);
}
vec3 normalForCube(vec3 hit, vec3 cubeMin, vec3 cubeMax) {
if (hit.x < cubeMin.x + 0.0001) return vec3(-1.0, 0.0, 0.0);
else if (hit.x > cubeMax.x - 0.0001) return vec3(1.0, 0.0, 0.0);
else if (hit.y < cubeMin.y + 0.0001) return vec3(0.0, -1.0, 0.0);
else if (hit.y > cubeMax.y - 0.0001) return vec3(0.0, 1.0, 0.0);
else if (hit.z < cubeMin.z + 0.0001) return vec3(0.0, 0.0, -1.0);
//else return vec3(0.0, 0.0, 1.0);
return vec3(0.0, 0.0, 1.0);
}
float intersectSphere(vec3 origin, vec3 ray, vec3 sphereCenter, float sphereRadius) {
vec3 toSphere = origin - sphereCenter;
float a = dot(ray, ray);
float b = 2.0 * dot(toSphere, ray);
float c = dot(toSphere, toSphere) - sphereRadius*sphereRadius;
float discriminant = b*b - 4.0*a*c;
if (discriminant > 0.0) {
float t = (-b - sqrt(discriminant)) / (2.0 * a);
if (t > 0.0) return t;
}
return 10000.0;
}
vec3 normalForSphere(vec3 hit, vec3 sphereCenter, float sphereRadius) {
return (hit - sphereCenter) / sphereRadius;
}
float random(vec3 scale, float seed) {
return fract(sin(dot(texCoord.xyx + seed, scale)) * 43758.5453 + seed);
// return fract(sin(dot(gl_FragCoord.xyz + seed, scale)) * 43758.5453 + seed);
}
vec3 cosineWeightedDirection(float seed, vec3 normal) {
float u = random(vec3(12.9898, 78.233, 151.7182), seed);
float v = random(vec3(63.7264, 10.873, 623.6736), seed);
float r = sqrt(u);
float angle = 6.283185307179586 * v;
// compute basis from normal
vec3 sdir, tdir;
if (abs(normal.x) < 0.5) {
sdir = cross(normal, vec3(1.0, 0.0, 0.0));
}
else {
sdir = cross(normal, vec3(0.0, 1.0, 0.0));
}
tdir = cross(normal, sdir);
return r*cos(angle)*sdir + r*sin(angle)*tdir + sqrt(1.0-u)*normal;
}
vec3 uniformlyRandomDirection(float seed) {
float u = random(vec3(12.9898, 78.233, 151.7182), seed);
float v = random(vec3(63.7264, 10.873, 623.6736), seed);
float z = 1.0 - 2.0 * u;
float r = sqrt(1.0 - z * z);
float angle = 6.283185307179586 * v;
return vec3(r * cos(angle), r * sin(angle), z);
}
vec3 uniformlyRandomVector(float seed) {
return uniformlyRandomDirection(seed) * sqrt(random(vec3(36.7539, 50.3658, 306.2759), seed));
}
float shadow(vec3 origin, vec3 ray) {
float tSphere0 = intersectSphere(origin, ray, sphereCenter0, sphereRadius0);
if (tSphere0 < 1.0) return 0.0;
float tSphere1 = intersectSphere(origin, ray, sphereCenter1, sphereRadius1);
if (tSphere1 < 1.0) return 0.0;
float tSphere2 = intersectSphere(origin, ray, sphereCenter2, sphereRadius2);
if (tSphere2 < 1.0) return 0.0;
float tSphere3 = intersectSphere(origin, ray, sphereCenter3, sphereRadius3);
if (tSphere3 < 1.0) return 0.0;
float tSphere4 = intersectSphere(origin, ray, sphereCenter4, sphereRadius4);
if (tSphere4 < 1.0) return 0.0;
float tSphere5 = intersectSphere(origin, ray, sphereCenter5, sphereRadius5);
if (tSphere5 < 1.0) return 0.0;
float tSphere6 = intersectSphere(origin, ray, sphereCenter6, sphereRadius6);
if (tSphere6 < 1.0) return 0.0;
float tSphere7 = intersectSphere(origin, ray, sphereCenter7, sphereRadius7);
if (tSphere7 < 1.0) return 0.0;
float tSphere8 = intersectSphere(origin, ray, sphereCenter8, sphereRadius8);
if (tSphere8 < 1.0) return 0.0;
float tSphere9 = intersectSphere(origin, ray, sphereCenter9, sphereRadius9);
if (tSphere9 < 1.0) return 0.0;
float tSphere10 = intersectSphere(origin, ray, sphereCenter10, sphereRadius10);
if (tSphere10 < 1.0) return 0.0;
float tSphere11 = intersectSphere(origin, ray, sphereCenter11, sphereRadius11);
if (tSphere11 < 1.0) return 0.0;
float tSphere12 = intersectSphere(origin, ray, sphereCenter12, sphereRadius12);
if (tSphere12 < 1.0) return 0.0;
float tSphere13 = intersectSphere(origin, ray, sphereCenter13, sphereRadius13);
if (tSphere13 < 1.0) return 0.0;
float tSphere14 = intersectSphere(origin, ray, sphereCenter14, sphereRadius14);
if (tSphere14 < 1.0) return 0.0;
float tSphere15 = intersectSphere(origin, ray, sphereCenter15, sphereRadius15);
if (tSphere15 < 1.0) return 0.0;
float tSphere16 = intersectSphere(origin, ray, sphereCenter16, sphereRadius16);
if (tSphere16 < 1.0) return 0.0;
float tSphere17 = intersectSphere(origin, ray, sphereCenter17, sphereRadius17);
if (tSphere17 < 1.0) return 0.0;
float tSphere18 = intersectSphere(origin, ray, sphereCenter18, sphereRadius18);
if (tSphere18 < 1.0) return 0.0;
float tSphere19 = intersectSphere(origin, ray, sphereCenter19, sphereRadius19);
if (tSphere19 < 1.0) return 0.0;
float tSphere20 = intersectSphere(origin, ray, sphereCenter20, sphereRadius20);
if (tSphere20 < 1.0) return 0.0;
float tSphere21 = intersectSphere(origin, ray, sphereCenter21, sphereRadius21);
if (tSphere21 < 1.0) return 0.0;
float tSphere22 = intersectSphere(origin, ray, sphereCenter22, sphereRadius22);
if (tSphere22 < 1.0) return 0.0;
float tSphere23 = intersectSphere(origin, ray, sphereCenter23, sphereRadius23);
if (tSphere23 < 1.0) return 0.0;
float tSphere24 = intersectSphere(origin, ray, sphereCenter24, sphereRadius24);
if (tSphere24 < 1.0) return 0.0;
float tSphere25 = intersectSphere(origin, ray, sphereCenter25, sphereRadius25);
if (tSphere25 < 1.0) return 0.0;
float tSphere26 = intersectSphere(origin, ray, sphereCenter26, sphereRadius26);
if (tSphere26 < 1.0) return 0.0;
float tSphere27 = intersectSphere(origin, ray, sphereCenter27, sphereRadius27);
if (tSphere27 < 1.0) return 0.0;
float tSphere28 = intersectSphere(origin, ray, sphereCenter28, sphereRadius28);
if (tSphere28 < 1.0) return 0.0;
float tSphere29 = intersectSphere(origin, ray, sphereCenter29, sphereRadius29);
if (tSphere29 < 1.0) return 0.0;
return 1.0;
}
vec3 calculateColor(float time, vec3 origin, vec3 ray, vec3 light) {
vec3 colorMask = vec3(1.0);
vec3 accumulatedColor = vec3(0.0);
// main raytracing loop
for (int bounce = 0; bounce < 5; bounce++) {
// compute the intersection with everything
vec2 tRoom = intersectCube(origin, ray, roomCubeMin, roomCubeMax);
float tSphere0 = intersectSphere(origin, ray, sphereCenter0, sphereRadius0);
float tSphere1 = intersectSphere(origin, ray, sphereCenter1, sphereRadius1);
float tSphere2 = intersectSphere(origin, ray, sphereCenter2, sphereRadius2);
float tSphere3 = intersectSphere(origin, ray, sphereCenter3, sphereRadius3);
float tSphere4 = intersectSphere(origin, ray, sphereCenter4, sphereRadius4);
float tSphere5 = intersectSphere(origin, ray, sphereCenter5, sphereRadius5);
float tSphere6 = intersectSphere(origin, ray, sphereCenter6, sphereRadius6);
float tSphere7 = intersectSphere(origin, ray, sphereCenter7, sphereRadius7);
float tSphere8 = intersectSphere(origin, ray, sphereCenter8, sphereRadius8);
float tSphere9 = intersectSphere(origin, ray, sphereCenter9, sphereRadius9);
float tSphere10 = intersectSphere(origin, ray, sphereCenter10, sphereRadius10);
float tSphere11 = intersectSphere(origin, ray, sphereCenter11, sphereRadius11);
float tSphere12 = intersectSphere(origin, ray, sphereCenter12, sphereRadius12);
float tSphere13 = intersectSphere(origin, ray, sphereCenter13, sphereRadius13);
float tSphere14 = intersectSphere(origin, ray, sphereCenter14, sphereRadius14);
float tSphere15 = intersectSphere(origin, ray, sphereCenter15, sphereRadius15);
float tSphere16 = intersectSphere(origin, ray, sphereCenter16, sphereRadius16);
float tSphere17 = intersectSphere(origin, ray, sphereCenter17, sphereRadius17);
float tSphere18 = intersectSphere(origin, ray, sphereCenter18, sphereRadius18);
float tSphere19 = intersectSphere(origin, ray, sphereCenter19, sphereRadius19);
float tSphere20 = intersectSphere(origin, ray, sphereCenter20, sphereRadius20);
float tSphere21 = intersectSphere(origin, ray, sphereCenter21, sphereRadius21);
float tSphere22 = intersectSphere(origin, ray, sphereCenter22, sphereRadius22);
float tSphere23 = intersectSphere(origin, ray, sphereCenter23, sphereRadius23);
float tSphere24 = intersectSphere(origin, ray, sphereCenter24, sphereRadius24);
float tSphere25 = intersectSphere(origin, ray, sphereCenter25, sphereRadius25);
float tSphere26 = intersectSphere(origin, ray, sphereCenter26, sphereRadius26);
float tSphere27 = intersectSphere(origin, ray, sphereCenter27, sphereRadius27);
float tSphere28 = intersectSphere(origin, ray, sphereCenter28, sphereRadius28);
float tSphere29 = intersectSphere(origin, ray, sphereCenter29, sphereRadius29);
// find the closest intersection
float t = 10000.0;
if (tRoom.x < tRoom.y) t = tRoom.y;
if (tSphere0 < t) t = tSphere0;
if (tSphere1 < t) t = tSphere1;
if (tSphere2 < t) t = tSphere2;
if (tSphere3 < t) t = tSphere3;
if (tSphere4 < t) t = tSphere4;
if (tSphere5 < t) t = tSphere5;
if (tSphere6 < t) t = tSphere6;
if (tSphere7 < t) t = tSphere7;
if (tSphere8 < t) t = tSphere8;
if (tSphere9 < t) t = tSphere9;
if (tSphere10 < t) t = tSphere10;
if (tSphere11 < t) t = tSphere11;
if (tSphere12 < t) t = tSphere12;
if (tSphere13 < t) t = tSphere13;
if (tSphere14 < t) t = tSphere14;
if (tSphere15 < t) t = tSphere15;
if (tSphere16 < t) t = tSphere16;
if (tSphere17 < t) t = tSphere17;
if (tSphere18 < t) t = tSphere18;
if (tSphere19 < t) t = tSphere19;
if (tSphere20 < t) t = tSphere20;
if (tSphere21 < t) t = tSphere21;
if (tSphere22 < t) t = tSphere22;
if (tSphere23 < t) t = tSphere23;
if (tSphere24 < t) t = tSphere24;
if (tSphere25 < t) t = tSphere25;
if (tSphere26 < t) t = tSphere26;
if (tSphere27 < t) t = tSphere27;
if (tSphere28 < t) t = tSphere28;
if (tSphere29 < t) t = tSphere29;
// info about hit
vec3 hit = origin + ray * t;
vec3 surfaceColor = vec3(0.75);
float specularHighlight = 0.0;
vec3 normal;
// calculate the normal (and change wall color)
if (t == tRoom.y) {
normal = -normalForCube(hit, roomCubeMin, roomCubeMax);
if (hit.x < -0.9999) surfaceColor = vec3(0.1, 0.5, 1.0); // blue
else if (hit.x > 0.9999) surfaceColor = vec3(1.0, 0.9, 0.1); // yellow
ray = cosineWeightedDirection(time + float(bounce), normal);
}
else if (t == 10000.0) {
break;
}
else {
int aa = 0;
if (aa == 1) {aa = 0;} // hack to discard the first 'else' in 'else if'
else if (t == tSphere0) normal = normalForSphere(hit, sphereCenter0, sphereRadius0);
else if (t == tSphere1) normal = normalForSphere(hit, sphereCenter1, sphereRadius1);
else if (t == tSphere2) normal = normalForSphere(hit, sphereCenter2, sphereRadius2);
else if (t == tSphere3) normal = normalForSphere(hit, sphereCenter3, sphereRadius3);
else if (t == tSphere4) normal = normalForSphere(hit, sphereCenter4, sphereRadius4);
else if (t == tSphere5) normal = normalForSphere(hit, sphereCenter5, sphereRadius5);
else if (t == tSphere6) normal = normalForSphere(hit, sphereCenter6, sphereRadius6);
else if (t == tSphere7) normal = normalForSphere(hit, sphereCenter7, sphereRadius7);
else if (t == tSphere8) normal = normalForSphere(hit, sphereCenter8, sphereRadius8);
else if (t == tSphere9) normal = normalForSphere(hit, sphereCenter9, sphereRadius9);
else if (t == tSphere10) normal = normalForSphere(hit, sphereCenter10, sphereRadius10);
else if (t == tSphere11) normal = normalForSphere(hit, sphereCenter11, sphereRadius11);
else if (t == tSphere12) normal = normalForSphere(hit, sphereCenter12, sphereRadius12);
else if (t == tSphere13) normal = normalForSphere(hit, sphereCenter13, sphereRadius13);
else if (t == tSphere14) normal = normalForSphere(hit, sphereCenter14, sphereRadius14);
else if (t == tSphere15) normal = normalForSphere(hit, sphereCenter15, sphereRadius15);
else if (t == tSphere16) normal = normalForSphere(hit, sphereCenter16, sphereRadius16);
else if (t == tSphere17) normal = normalForSphere(hit, sphereCenter17, sphereRadius17);
else if (t == tSphere18) normal = normalForSphere(hit, sphereCenter18, sphereRadius18);
else if (t == tSphere19) normal = normalForSphere(hit, sphereCenter19, sphereRadius19);
else if (t == tSphere20) normal = normalForSphere(hit, sphereCenter20, sphereRadius20);
else if (t == tSphere21) normal = normalForSphere(hit, sphereCenter21, sphereRadius21);
else if (t == tSphere22) normal = normalForSphere(hit, sphereCenter22, sphereRadius22);
else if (t == tSphere23) normal = normalForSphere(hit, sphereCenter23, sphereRadius23);
else if (t == tSphere24) normal = normalForSphere(hit, sphereCenter24, sphereRadius24);
else if (t == tSphere25) normal = normalForSphere(hit, sphereCenter25, sphereRadius25);
else if (t == tSphere26) normal = normalForSphere(hit, sphereCenter26, sphereRadius26);
else if (t == tSphere27) normal = normalForSphere(hit, sphereCenter27, sphereRadius27);
else if (t == tSphere28) normal = normalForSphere(hit, sphereCenter28, sphereRadius28);
else if (t == tSphere29) normal = normalForSphere(hit, sphereCenter29, sphereRadius29);
ray = reflect(ray, normal);
vec3 reflectedLight = normalize(reflect(light - hit, normal));
specularHighlight = max(0.0, dot(reflectedLight, normalize(hit - origin)));
specularHighlight = 2.0 * pow(specularHighlight, 20.0);
}
// compute diffuse lighting contribution
vec3 toLight = light - hit;
float diffuse = max(0.0, dot(normalize(toLight), normal));
// trace a shadow ray to the light
float shadowIntensity = shadow(hit + normal * 0.0001, toLight);
// do light bounce
colorMask *= surfaceColor;
accumulatedColor += colorMask * (0.8 * diffuse * shadowIntensity);
accumulatedColor += colorMask * specularHighlight * shadowIntensity;
// calculate next origin
origin = hit;
}
return accumulatedColor;
}
void main() {
float time = timeSinceStart;
vec3 col = vec3(0.0);
const int samples = 1;
for (int i = 0; i < samples; i++) {
vec3 newLight = light + uniformlyRandomVector(time - 53.0) * 0.1;
col += calculateColor(time, eye, initialRay, newLight);
time += 0.35;
}
gl_FragColor = vec4(vec3(col / samples), 1.0);
}

View file

@ -0,0 +1,38 @@
{
"contexts": [
{
"id": "pt_trace_pass",
"params": [
{
"id": "depth_write",
"value": "false"
},
{
"id": "compare_mode",
"value": "always"
},
{
"id": "cull_mode",
"value": "none"
},
{
"id": "blend_source",
"value": "blend_one"
},
{
"id": "blend_destination",
"value": "blend_zero"
}
],
"links": [
{
"id": "timeSinceStart",
"link": "_time"
}
],
"texture_params": [],
"vertex_shader": "pt_trace_pass.vert.glsl",
"fragment_shader": "pt_trace_pass.frag.glsl"
}
]
}

View file

@ -0,0 +1,24 @@
#version 450
#ifdef GL_ES
precision highp float;
#endif
in vec2 pos;
uniform vec3 ray00;
uniform vec3 ray01;
uniform vec3 ray10;
uniform vec3 ray11;
out vec3 initialRay;
out vec2 texCoord;
const vec2 madd = vec2(0.5, 0.5);
void main() {
// Scale vertex attribute to [0-1] range
texCoord = pos.xy * madd + madd;
initialRay = mix(mix(ray00, ray01, texCoord.y), mix(ray10, ray11, texCoord.y), texCoord.x);
gl_Position = vec4(pos.xy, 0.0, 1.0);
}