Skip to content

Commit

Permalink
[cl] Implement refractive BTDF
Browse files Browse the repository at this point in the history
  • Loading branch information
achilleasa committed Aug 11, 2016
1 parent 03b13eb commit 518c0de
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 4 deletions.
8 changes: 7 additions & 1 deletion scene/compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -330,11 +330,17 @@ func (sc *sceneCompiler) createLayeredMaterialTrees() error {
node.SetNormalTex(mat.NormalTex)
node.SetBxdfType(scene.Specular)
sc.optimizedScene.MaterialNodeList = append(sc.optimizedScene.MaterialNodeList, node)
} else if isRefractive && !(isDiffuse || isSpecular || isEmissive) {
} else if isRefractive && !(isDiffuse || isEmissive) {
// Ideal refractive surface
node = scene.MaterialNode{}
node.Init()

if mat.Ks.MaxComponent() == 0.0 && mat.KsTex == -1 {
node.Kval = types.Vec4{1.0, 1.0, 1.0, 0.0}
} else {
node.Kval = mat.Ks.Vec4(0)
node.SetKvalTex(mat.KsTex)
}
node.SetNormalTex(mat.NormalTex)
node.SetBxdfType(scene.Refractive)
node.Nval = mat.Ni
Expand Down
10 changes: 10 additions & 0 deletions tracer/opencl/CL/bxdf/bxdf.cl
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@

#include "lambert.cl"
#include "ideal_specular.cl"
#include "refractive.cl"

#define BXDF_TYPE_DIFFUSE 1 << 0
#define BXDF_TYPE_SPECULAR 1 << 1
#define BXDF_TYPE_REFRACTIVE 1 << 2
#define BXDF_TYPE_EMISSIVE 1 << 3

// Returns true if BXDF models a transmission
#define BXDF_IS_TRANSMISSION(t) (t == BXDF_TYPE_REFRACTIVE)

float3 bxdfGetSample(Surface *surface, MaterialNode *matNode, __global TextureMetadata *texMeta, __global uchar *texData, float2 randSample, float3 inRayDir, float3 *outRayDir, float *pdf);
float bxdfGetPdf(Surface *surface, MaterialNode *matNode, __global TextureMetadata *texMeta, __global uchar *texData, float3 inRayDir, float3 outRayDir );
float3 bxdfEval(Surface *surface, MaterialNode *matNode, __global TextureMetadata *texMeta, __global uchar *texData, float3 inRayDir, float3 outRayDir);
Expand All @@ -30,6 +34,8 @@ float3 bxdfGetSample(
return lambertDiffuseSample(surface, matNode, texMeta, texData, randSample, outRayDir, pdf);
case BXDF_TYPE_SPECULAR:
return idealSpecularSample(surface, matNode, texMeta, texData, randSample, inRayDir, outRayDir, pdf);
case BXDF_TYPE_REFRACTIVE:
return refractiveSample(surface, matNode, texMeta, texData, randSample, inRayDir, outRayDir, pdf);
}

return (float3)(0.0f, 0.0f, 0.0f);
Expand All @@ -50,6 +56,8 @@ float bxdfGetPdf(
return lambertDiffusePdf(surface, matNode, outRayDir);
case BXDF_TYPE_SPECULAR:
return idealSpecularPdf();
case BXDF_TYPE_REFRACTIVE:
return refractivePdf();
}

return 0.0f;
Expand All @@ -70,6 +78,8 @@ float3 bxdfEval(
return lambertDiffuseEval(surface, matNode, texMeta, texData, outRayDir);
case BXDF_TYPE_SPECULAR:
return idealSpecularEval();
case BXDF_TYPE_REFRACTIVE:
return refractiveEval();
}

return (float3)(0.0f, 0.0f, 0.0f);
Expand Down
65 changes: 65 additions & 0 deletions tracer/opencl/CL/bxdf/refractive.cl
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#ifndef BXDF_REFRACTIVE_CL
#define BXDF_REFRACTIVE_CL

float3 refractiveSample(Surface *surface, MaterialNode *matNode, __global TextureMetadata *texMeta, __global uchar *texData, float2 randSample, float3 rayInDir, float3 *rayOutDir, float *pdf);
float refractivePdf();
float3 refractiveEval();

// Sample ideal diffuse (refractive) surface:
//
// BXDF = 1 / cos(theta)
// PDF = 1
float3 refractiveSample(Surface *surface, MaterialNode *matNode, __global TextureMetadata *texMeta, __global uchar *texData, float2 randSample, float3 rayInDir, float3 *rayOutDir, float *pdf){
*pdf = 1.0f;

// rayInDir point *away* from surface
float cosI = dot(rayInDir, surface->normal);
bool entering = cosI > 0.0f;

float etaI = 1.0f;
float etaT = matGetSample1f(surface->uv, matNode->nval, matNode->nvalTex, texMeta, texData);
float3 surfNormal = surface->normal;

// If we are exiting the area we need to flip the normal and incident/transmission etas
if( !entering ) {
etaI = etaT;
etaT = 1.0f;
surfNormal = -surfNormal;
cosI = -cosI;
}

// The following formulas are taken from: https://en.wikipedia.org/wiki/Refractive_index
float eta = etaI / etaT;
float sqISinI = 1.0f - cosI * cosI;
float sqSinT = eta * eta * sqISinI;

// According to Snell's law:
// etaI * sinI = etaT * sinT <=> sinT = eta * sinI
// If sinT > 1 then the ray undergoes total internal refleciton
if( sqSinT > 1.0f ){
return (float3)(0.0f, 0.0f, 0.0f);
}

// cos = 1 - sin*sin
float cosTransimission = native_sqrt(max(0.0f, 1.0f - sqSinT));
float sinT = native_sqrt(max(0.0f, sqSinT));
*rayOutDir = normalize(normalize(surfNormal * cosI - rayInDir) * sinT - surfNormal * cosTransimission);

float3 ks = eta * eta * matGetSample3f(surface->uv, matNode->kval, matNode->kvalTex, texMeta, texData);
return cosTransimission > 0.0f ? matNode->fresnel * ks / cosTransimission: 0.0f;
}

// Get PDF for refractive surface given a pre-calculated bounce ray.
// PDF = 0 unless rayOutDir = refract(rayInDir, surface->normal)
float refractivePdf(){
// Cheat and always return 0
return 0.0f;
}

// Evaluate BXDF for refractive surface given a pre-calculated bounce ray.
// Similar to the PDF evaluator above this is always 0 unless we use the
// refracted ray.
float3 refractiveEval(){
return (float3)(0.0f, 0.0f, 0.0f);
}
#endif
15 changes: 12 additions & 3 deletions tracer/opencl/CL/kernels/pt_integrator.cl
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,13 @@ __kernel void shadeHits(
surface.normal = matGetNormalSample3f(surface.normal, surface.uv, materialNode.normalTex, texMeta, texData);
}

float inRayDotNormal = dot(inRayDir, surface.normal);

// Check if we hit an emissive node. If so, we need to accumulate implicit
// light and terminate the path.
if( MAT_IS_EMISSIVE(materialNode) ){
// Make sure that the incoming ray is facing the emissive
if( dot( inRayDir, surface.normal ) > 0.0f ){
if( inRayDotNormal > 0.0f ){
accumulator[rayPathIndex] += curPathThroughput * matGetSample3f(surface.uv, materialNode.kval, materialNode.kvalTex, texMeta, texData);
}
} else {
Expand Down Expand Up @@ -144,10 +146,15 @@ __kernel void shadeHits(
&bxdfPdf
);


// If this material is refractive and we are hitting it from the outside
// we need to ensure that the outgoing ray starts inside the surface.
float displaceDir = BXDF_IS_TRANSMISSION(materialNode.bxdfType) ? -sign(inRayDotNormal) : 1.0f;

// To calculate the origin for occlusion/indirect rays we displace the
// surface hit point by a small epsilon along the normal to ensure that
// we don't register an intersection with the same surface
outRayOrigin = DISPLACE_BY_EPSILON(surface.point, surface.normal);
outRayOrigin = DISPLACE_BY_EPSILON(surface.point, surface.normal * displaceDir);

// Select and sample emissive source
int emissiveIndex = numEmissives > 0 ? emissiveSelect(numEmissives, sample1.x, &emissiveSelectionPdf) : -1;
Expand Down Expand Up @@ -207,7 +214,9 @@ __kernel void shadeHits(
}

// If we got a valid bxdf sample update the path throughput
float3 throughput = bxdfSample * dot(surface.normal, bxdfOutRayDir) * bxdfWeight;
// Note: we are using the abs value of the dot product as
// it will be negative for rays entering into refractive surfaces
float3 throughput = bxdfSample * fabs(dot(surface.normal, bxdfOutRayDir)) * bxdfWeight;

if (MIN_VEC3_COMPONENT(throughput) > 0.0f && bxdfPdf > 0.0f){
pathSetThroughput(paths + rayPathIndex, curPathThroughput * throughput / bxdfPdf);
Expand Down

0 comments on commit 518c0de

Please sign in to comment.