Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
378 lines (295 sloc) 10.3 KB
#include "common.hlsl"
struct PSInput {
float4 pos : SV_POSITION;
float4 color : TEXCOORD0;
float4 coordinates : TEXCOORD1;
float4 reflectance : TEXCOORD2;
};
struct Intersection {
float3 pos;
float3 norm;
float theta;
bool hit;
bool inverted;
};
struct LensInterface {
float3 center;
float radius;
float3 n;
float sa;
float d1;
float flat;
float pos;
float w;
};
struct Ray {
float3 pos;
float3 dir;
float4 tex;
};
struct GhostData{
float bounce1;
float bounce2;
float2 padding;
};
StructuredBuffer<PSInput> vertices_buffer : register(t0);
RWStructuredBuffer<PSInput> uav_buffer : register(u0);
RWStructuredBuffer<LensInterface> lens_interface : register(u1);
RWStructuredBuffer<GhostData> ghostdata_buffer : register(u2);
Intersection TestFlat(Ray r, LensInterface F) {
Intersection i;
i.pos = r.pos + r.dir * ((F.center.z - r.pos.z) / r.dir.z);
i.norm = r.dir.z > 0 ? float3(0, 0, -1) : float3(0, 0, 1);
i.theta = 0;
i.hit = true;
i.inverted = false;
return i;
}
Intersection TestSphere(Ray r, LensInterface F) {
Intersection i;
float3 D = r.pos - F.center;
float B = dot(D, r.dir);
float C = dot(D, D) - F.radius * F.radius;
float B2_C = B*B-C;
if (B2_C < 0) {
i.hit = false;
return i;
}
float sgn = (F.radius * r.dir.z) > 0 ? 1.f : -1.f;
float t = sqrt(B2_C) * sgn - B;
i.pos = r.dir * t + r.pos;
i.norm = normalize(i.pos - F.center);
if (dot(i.norm, r.dir) > 0)
i.norm = -i.norm;
float d = clamp(-1, 1, dot(-r.dir, i.norm));
i.theta = acos(d);
i.hit = true;
i.inverted = t < 0;
return i;
}
float FresnelAR(float theta0, float lambda, float d1, float n0, float n1, float n2) {
float theta1 = asin(sin(theta0) *n0 / n1);
float theta2 = asin(sin(theta0) *n0 / n2);
float rs01 = -sin(theta0-theta1) / sin(theta0+theta1);
float rp01 = tan(theta0-theta1) / tan(theta0+theta1);
float ts01 = 2*sin(theta1) *cos(theta0) / sin(theta0+theta1);
float tp01 = ts01*cos(theta0-theta1);
float rs12 = -sin(theta1-theta2) / sin(theta1+theta2);
float rp12 = +tan(theta1-theta2) / tan(theta1+theta2);
float ris = ts01*ts01*rs12;
float rip = tp01*tp01*rp12;
float dy = d1*n1 ;
float dx = tan(theta1) *dy;
float delay = sqrt(dx*dx+dy*dy);
float relPhase = 4*PI / lambda*(delay-dx*sin(theta0) );
float out_s2 = rs01*rs01 + ris*ris + 2*rs01*ris*cos(relPhase);
float out_p2 = rp01*rp01 + rip*rip + 2*rp01*rip*cos(relPhase);
return (out_s2+out_p2) / 2 ;
}
Ray Trace(Ray r, float lambda, int2 bounce_pair) {
int LEN = bounce_pair.x + (bounce_pair.x - bounce_pair.y) + (num_interfaces - bounce_pair.y) - 1;
int PHASE = 0;
int DELTA = 1;
int T = 1;
int k;
for(k=0; k < LEN; k++, T += DELTA) {
LensInterface F = lens_interface[T];
bool bReflect = (T == bounce_pair[PHASE]) ? true : false;
if (bReflect) { DELTA = -DELTA; PHASE++; } // intersection test
Intersection i;
if(F.flat)
i = TestFlat(r, lens_interface[T]);
else
i = TestSphere(r, lens_interface[T]);
[branch]
if (!i.hit) {
r.pos = 0;
r.tex.a = 0;
break; // exit upon miss
}
// record texture coord . or max. rel . radius
if (!F.flat)
r.tex.z = max(r.tex.z, length(i.pos.xy) / F.sa);
else if(T==AP_IDX) // iris aperture plane
r.tex.xy = i.pos.xy / lens_interface[AP_IDX].sa; // update ray light_dir and position
r.dir = normalize(i.pos- r.pos);
if (i.inverted) r.dir *= -1.f; // correct an ← inverted ray
r.pos = i.pos;
// skip reflection / refraction for flat surfaces
if (F.flat)
continue;
// do reflection / refraction for spher . surfaces
float n0 = r.dir.z < 0.f ? F.n.x : F.n.z;
float n2 = r.dir.z < 0.f ? F.n.z : F.n.x;
if (!bReflect) { // refraction
r.dir = refract(r.dir,i.norm,n0/n2);
[branch]
if(length(r.dir) == 0){
r.pos = 0;
r.tex.a = 0;
break; // total reflection
}
} else { // reflection with AR Coating
r.dir = reflect(r.dir,i.norm);
float n1 = max(sqrt(n0*n2) , 1.38 + coating_quality);
float d1 = (F.d1 * NANO_METER);
float R = FresnelAR(i.theta + 0.001, lambda, d1, n0, n1, n2);
R = saturate(R);
r.tex.a *= R; // update ray intensity
}
}
[branch]
if (k<LEN) {
r.pos = 0;
r.tex.a = 0; // early-exit rays = invalid
}
return r;
}
uint PosToOffset(int2 pos) {
return pos.x + pos.y * PATCH_TESSELATION;
}
uint PosToOffsetClamped(int2 pos) {
int x = clamp(pos.x, 0, PATCH_TESSELATION - 1);
int y = clamp(pos.y, 0, PATCH_TESSELATION - 1);
return x + y * PATCH_TESSELATION;
}
float GetArea(int2 pos, int offset) {
// a----b----c
// | A | B |
// d----e----f
// | C | D |
// g----h----i
int a = PosToOffsetClamped(pos + int2(-1, 1));
int b = PosToOffsetClamped(pos + int2( 0, 1));
int c = PosToOffsetClamped(pos + int2( 1, 1));
int d = PosToOffsetClamped(pos + int2(-1, 0));
int e = PosToOffsetClamped(pos + int2( 0, 0));
int f = PosToOffsetClamped(pos + int2( 1, 0));
int g = PosToOffsetClamped(pos + int2(-1,-1));
int h = PosToOffsetClamped(pos + int2( 0,-1));
int i = PosToOffsetClamped(pos + int2( 1,-1));
// Should use groupshared to access neighbors
float4 pa = uav_buffer[a + offset].pos;
float4 pb = uav_buffer[b + offset].pos;
float4 pc = uav_buffer[c + offset].pos;
float4 pd = uav_buffer[d + offset].pos;
float4 pe = uav_buffer[e + offset].pos;
float4 pf = uav_buffer[f + offset].pos;
float4 pg = uav_buffer[g + offset].pos;
float4 ph = uav_buffer[h + offset].pos;
float4 pi = uav_buffer[i + offset].pos;
float ab = length(pa.xy - pb.xy);
float bc = length(pb.xy - pc.xy);
float ad = length(pa.xy - pd.xy);
float be = length(pb.xy - pe.xy);
float cf = length(pc.xy - pf.xy);
float de = length(pd.xy - pe.xy);
float ef = length(pe.xy - pf.xy);
float dg = length(pd.xy - pg.xy);
float eh = length(pe.xy - ph.xy);
float fi = length(pf.xy - pi.xy);
float gh = length(pg.xy - ph.xy);
float hi = length(ph.xy - pi.xy);
bool left_edge = (pos.x == 0);
bool right_edge = (pos.x == (PATCH_TESSELATION - 1));
bool bottom_edge = (pos.y == 0);
bool top_edge = (pos.y == (PATCH_TESSELATION - 1));
float A = lerp(ab, de, 0.5f) * lerp(ad, be, 0.5f) * (!left_edge && !top_edge);
float B = lerp(bc, ef, 0.5f) * lerp(be, cf, 0.5f) * (!right_edge && !top_edge);
float C = lerp(de, gh, 0.5f) * lerp(dg, eh, 0.5f) * (!left_edge && !bottom_edge);
float D = lerp(ef, hi, 0.5f) * lerp(eh, fi, 0.5f) * (!right_edge && !bottom_edge);
bool is_edge = (left_edge || right_edge) || (bottom_edge || top_edge);
bool is_corner = (left_edge || right_edge) && (bottom_edge || top_edge);
float no_area_contributors = is_corner ? 1.f : is_edge ? 2.f : 4.f;
float unit_patch_length = spread / (float)PATCH_TESSELATION;
float Oa = unit_patch_length * unit_patch_length * no_area_contributors;
float Na = (A + B + C + D) / no_area_contributors;
float energy = 4.f;
float area = (Oa/(Na + 0.00001)) * energy;
return isnan(area) ? 0.f : area;
}
PSInput GetTraceResult(float2 ndc, float wavelength, int2 bounces){
float3 starting_pos = float3(ndc * spread, 1000.f);
starting_pos.xy = Rotate(starting_pos.xy, 2.f);
// Project all starting points in the entry lens
Ray c = { starting_pos, float3(0, 0, -1.f), float4(0,0,0,0) };
Intersection i = TestSphere(c, lens_interface[0]);
starting_pos = i.pos - light_dir.xyz;
Ray r = { starting_pos, light_dir.xyz, float4(0,0,0,1) };
Ray g = Trace(r, wavelength, bounces);
PSInput result;
result.pos = float4(g.pos.xyz, 1.f);
result.coordinates = float4(ndc, g.tex.xy);
result.color = g.tex;
result.reflectance = float4(0,0,0, g.tex.a);
return result;
}
// Compute Shader
// ----------------------------------------------------------------------------------
[numthreads(NUM_THREADS, NUM_THREADS, 1)]
void CS(int3 gid : SV_GroupID, uint3 gtid : SV_GroupThreadID, uint gi : SV_GroupIndex) {
int ghostid = gid.x / NUM_GROUPS;
int data_offset = ghostid * PATCH_TESSELATION * PATCH_TESSELATION;
int2 pos = gtid.xy + (gid.xy % NUM_GROUPS) * NUM_THREADS;
float2 uv = pos / float(PATCH_TESSELATION - 1);
float2 ndc = (uv - 0.5f) * 2.f;
float color_spectrum[3] = {650.f, 510.f, 475.f};
float wavelength = color_spectrum[gid.z] * NANO_METER;
int2 bounces = int2(ghostdata_buffer[ghostid].bounce1, ghostdata_buffer[ghostid].bounce2);
PSInput result = GetTraceResult(ndc, wavelength, bounces);
uint offset = PosToOffset(pos) + data_offset;
uav_buffer[offset].reflectance.a = 0;
// AllMemoryBarrierWithGroupSync();
uav_buffer[offset].pos = result.pos;
uav_buffer[offset].color = result.color;
uav_buffer[offset].coordinates = result.coordinates;
if(gid.z == 0)
uav_buffer[offset].reflectance.r = result.reflectance.a;
else if(gid.z == 1)
uav_buffer[offset].reflectance.g = result.reflectance.a;
else if(gid.z == 2)
uav_buffer[offset].reflectance.b = result.reflectance.a;
uav_buffer[offset].color.w = GetArea(pos, data_offset);
}
// Vertex Shader
// ----------------------------------------------------------------------------------
PSInput VS(uint id : SV_VertexID, uint instance_id : SV_InstanceID) {
PSInput vertex = vertices_buffer[id + instance_id * PATCH_TESSELATION * PATCH_TESSELATION];
float ratio = backbuffer_size.x / backbuffer_size.y;
float scale = 1.f / plate_size;
vertex.pos.xy *= scale * float2(1.f, ratio);
vertex.pos.w = 1;
return vertex;
}
// Pixel Shader
// ----------------------------------------------------------------------------------
float4 PS(in PSInput input) : SV_Target {
float4 color = input.color;
float4 coordinates = input.coordinates;
float2 aperture_uv = (coordinates.zw + 1.f)/2.f;
float aperture = input_texture1.Sample(LinearSampler, aperture_uv).b;
float fade = 0.2;
float lens_distance = length(coordinates.xy);
float sun_disk = 1 - saturate((lens_distance - 1.f + fade)/fade);
sun_disk = smoothstep(0, 1, sun_disk);
sun_disk *= lerp(0.5, 1, saturate(lens_distance));
float aperture_disk = saturate(length(aperture_uv - 0.5) * 0.5);
aperture_disk = smoothstep(0, 1, aperture_disk);
aperture_disk = lerp(0.5, 1, aperture_disk);
float alpha1 = color.z < 1.0f;
float alpha2 = sun_disk;
float alpha3 = color.w;
float alpha4 = aperture;
float alpha5 = aperture_disk;
float alpha = alpha1 * alpha2 * alpha3 * alpha4 * alpha5;
if(alpha == 0.f)
discard;
#if defined(DEBUG_WIREFRAME)
return float4(aperture_uv, 0.0, 1);
#endif
#if defined(DEBUG_VALUES)
return float4(alpha, alpha, alpha ,1);
#endif
float3 v = alpha * input.reflectance.xyz * TemperatureToColor(INCOMING_LIGHT_TEMP);
return float4(v, 1.f);
}