HDRP PostProcessing Raymarching
TODO test 2020.1.6f1
Custom Passを使ってスクリーンスペースレイマーチングを描きます。
https://gyazo.com/c50c1d781ed977569fc336cd3a4eefab
Kanetaaaaa神とGam師のレイマーチングをHDRP用のHLSLにしたコードを以下に記載。
code:HdrpPostProcessingRaymarching.HLSL
Shader "FullScreen/FullScreenRaymarching"
{
HLSLINCLUDE
#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" static float pi = 3.141592;
static float pi2 = pi * 2.0;
float mod(float x, float y)
{
return x - y * floor(x / y);
}
float2 mod(float2 x, float2 y)
{
return x - y * floor(x / y);
}
float3 mod(float3 x, float3 y)
{
return x - y * floor(x / y);
}
float4 mod(float4 x, float4 y)
{
return x - y * floor(x / y);
}
float2 min2(float2 a, float2 b) {
return a.x < b.x ? a : b;
}
float2 rot(float2 p, float a) {
return float2(p.x*cos(a) + p.y*sin(a), -p.x*sin(a) + p.y*cos(a));
}
float2 pmod(float2 p, float r) {
float a = pi / r - atan2(p.x, p.y);
float n = pi2 / r;
a = floor(a / n)*n;
return rot(p, a);
}
float fOpPipe(float a, float b, float r) {
return length(float2(a, b)) - r;
}
float fOpUnionStairs(float a, float b, float r, float n) {
float s = r / n;
float u = b - r;
return min(min(a, b), 0.5 * (u + a + abs((mod(u - a + s, 2. * s)) - s)));
}
float fOpDifferenceStairs(float a, float b, float r, float n) {
return -fOpUnionStairs(-a, b, r, n);
}
float sdSphere(float3 p, float r) {
return length(p) - r;
}
float sdCappedCylinder(float3 p, float r, float h) {
float d = length(p.xz) - r;
p.y = abs(p.y) - h;
return max(d, p.y);
}
float sdTorus(float3 p, float r, float s) {
float2 q = float2(length(p.xz) - s, p.y);
return length(q) - r;
}
float smoothPulse(float start, float end, float period, float t, float smooth) {
float halfp = .5*period;
t = 2.*abs(mod(t, period) - halfp) - 1.;
return smoothstep(start, start + smooth, t) - smoothstep(end - smooth, end, t);
}
float energyAnim(float z) {
return smoothPulse(0.0, 0.6, 6.0, z + _Time.y, 0.2);
}
float2 map(float3 p) {
float sphere = sdSphere(p - float3(0.0, cos(_Time.y) * 1., 0.0), .5);
p.y -= 2.0;
// 上に反射した時に真っ暗なのはさみしいので、フォールドして空間を閉じる
p.y = -abs(p.y);
float flor = fOpDifferenceStairs(p.y, sdCappedCylinder(p, 8.0, 4.0), 4.0, 15.);
flor = fOpUnionStairs(flor, sdCappedCylinder(p - float3(0.0, -4.0, 0.0), 1.0, 1.0), 2.0, 5.);
float3 q = p;
q.xz = pmod(q.xz, 12. + 8.*sin(_Time.y));
float pipe = fOpPipe(flor - 0.05, abs(q.x + sin(q.z*2.0) * 0.1), 0.07*(6. + abs(sin(_Time.y))));
float2 tile = frac(p.xz * 8.0) * 2.0 - 1.0;
tile = abs(tile) - 0.5;
float tileHeight = max(max(tile.x, tile.y), 0.0);
float pipeHeight = smoothPulse(0.04, 0.06, 0.1, q.z, 0.01);
float2 d = min2(float2(flor + tileHeight * 0.02, MAT_FLOOR), float2(sphere, MAT_BALL));
d = min2(d, float2(pipe + pipeHeight * 0.02 - energyAnim(q.z) * 0.05, MAT_PIPE));
return d;
}
float roughnessToExponent(float roughness)
{
return clamp(2.0 * (1.0 / (roughness * roughness)) - 2.0, FLT_EPS, 1.0 / FLT_EPS);
}
float3 light(float3 p, float3 n, float3 v, float3 lp, float3 baseColor, float roughness, float reflectance, float metallic, float3 radiance) {
float3 ref = lerp((reflectance).xxx, baseColor, metallic);
float3 l = lp - p;
float len = length(l);
l /= len;
float3 h = normalize(l + v);
float3 diffuse = lerp(1.0 - ref, (0.0).xxx, metallic) * baseColor / pi;
float m = roughnessToExponent(roughness);
float3 specular = ref * pow(max(0.0, dot(n, h)), m) * (m + 2.0) / (8.0 * pi);
return (diffuse + specular) * radiance * max(0.0, dot(l, n)) / (len*len);
}
float3 cameraPos;
float3 evalLights(float3 p, float3 n, float3 ray, float3 baseColor, float roughness, float reflectance, float metallic) {
// Object Light
float3 lp = float3(0.0, cos(_Time.y) * 0.1, 0.0);
float3 result = light(p, n, -ray, lp, baseColor, roughness, reflectance, metallic, float3(0.6, 0.05, 0.01) * (sin(_Time.y) * 0.5 + 0.5) * 8.0);
// Camera Light
result += light(p, n, -ray, cameraPos, baseColor, roughness, reflectance, metallic, (3.0).xxx);
return result;
}
void getSurfaceParams(float3 p, float2 mat, out float3 outColor, out float3 outEmission, out float outRoughness, out float outReflectance, out float outMetallic) {
outColor = (0.0).xxx;
outEmission = (0.0).xxx;
outRoughness = 1.0;
outReflectance = 0.04;
outMetallic = 0.0;
if (mat.y == MAT_FLOOR) {
outColor = (0.5).xxx;
outRoughness = 0.2;
}
else if (mat.y == MAT_PIPE) {
outColor = (0.9).xxx;
outRoughness = 0.15;
outMetallic = 1.0;
p.xz = pmod(p.xz, 5.0);
float energy = energyAnim(p.z);
outEmission = lerp(float3(0.6, 0.05, 0.01), float3(0.01, 0.05, 0.6), clamp(p.z * 0.2, 0.0, 1.0)) * 4.0 * energy;
}
else if (mat.y == MAT_BALL) {
outColor = float3(.1, .01, .0001);
outEmission = float3(0.06, 0.005, 0.001) * -1.*abs(.25*sin(_Time.y));
outRoughness = .01;
}
}
float3 normal(float3 p) {
float2 e = float2(1.0, -1.0) * 0.001;
return normalize(
e.xyy * map(p + e.xyy).x +
e.yxy * map(p + e.yxy).x +
e.yyx * map(p + e.yyx).x +
e.xxx * map(p + e.xxx).x
);
}
// 反射時にもう一度レイマーチングをするので、使いまわせるように関数化する
void trace(float3 p, float3 ray, out float3 outPos, out float2 outMat) {
float t = 0.01;
float3 pos;
float2 mat;
for (int i = 0; i < 99; i++) {
pos = p + ray * t;
mat = map(pos);
if (mat.x < 0.0001) {
break;
}
t += mat.x;
}
outPos = pos;
outMat = mat;
}
// 衝突点の二次反射を含めたシェーディングを行う関数
float3 shade(float3 pos, float2 mat, float3 ray) {
// 一次光源のライティングを行う
float3 color, emission;
float roughness, reflectance, metallic;
getSurfaceParams(pos, mat, color, emission, roughness, reflectance, metallic);
float3 n = normal(pos);
float3 result = evalLights(pos, n, ray, color, roughness, reflectance, metallic) + emission;
// 二次反射の合成率に使用するため、反射率を取得する
float3 ref = lerp((reflectance).xxx, color, metallic);
float3 secondPos;
float2 secondMat;
// 視線と法線の反射ベクトルを計算する
ray = reflect(ray, n);
// 二次反射のトレースとライティングを行う
trace(pos, ray, secondPos, secondMat);
getSurfaceParams(secondPos, secondMat, color, emission, roughness, reflectance, metallic);
n = normal(secondPos);
result += (evalLights(secondPos, n, ray, color, roughness, reflectance, metallic) + emission) * ref;
return result;
}
float4 FullScreenPass(Varyings varyings) : SV_Target
{
float depth = LoadCameraDepth(varyings.positionCS.xy);
PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
float3 viewDirection = GetWorldSpaceNormalizeViewDir(posInput.positionWS);
float4 color = float4(0.0, 0.0, 0.0, 1.0);
// Load the camera color buffer at the mip 0 if we're not at the before rendering injection point
if (_CustomPassInjectionPoint != CUSTOMPASSINJECTIONPOINT_BEFORE_RENDERING)
color = float4(CustomPassSampleCameraColor(posInput.positionNDC.xy, 0), 1);
// Add your custom pass code here
//float2 p = (gl_FragCoord.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
float2 p = (2.*varyings.positionCS.xy - _ScreenSize.xy) / min(_ScreenSize.x, _ScreenSize.y);
cameraPos = float3(sin(_Time.y * 0.1) + cos(_Time.y * 0.5) * 0.5, sign(mod(_Time.y, 4.) - 2.), -5.0);
cameraPos.xz = rot(cameraPos.xz, _Time.y*sign(mod(_Time.y, 4.) - 2.));
float3 targetPos = float3(0.5, 1. + sin(_Time.y), 0.5);
targetPos.xz = rot(targetPos.xz, _Time.y);
float3 forward = normalize(targetPos - cameraPos);
float3 right = normalize(cross(float3(0.0, 1.0, 0.0), forward));
float3 up = normalize(cross(forward, right));
float3 ray = normalize(forward * (2.5 + (sin(_Time.y * 0.5)*0.5 + 0.5)*2.0) + right * p.x + up * p.y);
// スフィアトレーシングとマテリアルライティングを関数化した
float3 surfacePos;
float2 surfaceMat;
trace(cameraPos, ray, surfacePos, surfaceMat);
float3 col = shade(surfacePos, surfaceMat, ray);
float ints = 2.*(cos(_Time.y) + 1.5);
col = ints * pow(abs(col), (.4545).xxx);
col *= .5 + .5*dot(.75*p, .75*p);
color.rgb = col;
return color;
}
ENDHLSL
SubShader
{
Pass
{
Name "Custom Pass4"
ZWrite Off
ZTest Always
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
HLSLPROGRAM
ENDHLSL
}
}
Fallback Off
}
https://gyazo.com/242fdf98b6e5530c178e34106511bb29
code:Shinto Shrine Archway.HLSL
Shader "FullScreen/March4"
{
HLSLINCLUDE
#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" static float EPS = 1e-4;
static float OFFSET = EPS * 10.0;
//static float PI = 3.14159;
static float INF = 1e+10;
static float3 lightDir = float3(-0.48666426339228763, 0.8111071056538127, -0.3244428422615251);
static float3 backgroundColor = (0.0).xxx;
static float3 gateColor = float3(1.0, 0.1, 0.1);
static float totalTime = 75.0;
static int BASIC_MATERIAL = 0;
static int MIRROR_MATERIAL = 1;
float3 cPos, cDir;
float normalizedGlobalTime = 0.0;
struct Intersect {
bool isHit;
float3 position;
float distance;
float3 normal;
int material;
float3 color;
};
float mod(float x, float y)
{
return x - y * floor(x / y);
}
float2 mod(float2 x, float y)
{
return x - y * floor(x / y);
}
float3 mod(float3 x, float y)
{
return x - y * floor(x / y);
}
// distance functions
float3 opRep(float3 p, float interval) {
return mod(p, interval) - 0.5 * interval;
}
float2 opRep(float2 p, float interval) {
return mod(p, interval) - 0.5 * interval;
}
float opRep(float x, float interval) {
return mod(x, interval) - 0.5 * interval;
}
float sphereDist(float3 p, float3 c, float r) {
return length(p - c) - r;
}
float sdCappedCylinder(float3 p, float2 h) {
float2 d = abs(float2(length(p.xz), p.y)) - h;
return min(max(d.x, d.y), 0.0) + length(max(d, 0.0));
}
float udBox(float3 p, float3 b)
{
return length(max(abs(p) - b, 0.0));
}
float udFloor(float3 p) {
float t1 = 1.0;
float t2 = 3.0;
float d = -0.5;
for (float i = 0.0; i < 3.0; i++) {
float f = pow(2.0, i);
d += 0.1 / f * (sin(f * t1 * p.x + t2 * _Time.y) + sin(f * t1 * p.z + t2 * _Time.y));
}
return dot(p, float3(0.0, 1.0, 0.0)) - d;
}
float dGate(float3 p) {
p.y -= 1.3 * 0.5;
float r = 0.05;
float left = sdCappedCylinder(p - float3(-1.0, 0.0, 0.0), float2(r, 1.3));
float right = sdCappedCylinder(p - float3(1.0, 0.0, 0.0), float2(r, 1.3));
float ty = 0.02 * p.x * p.x;
float tx = 0.5 * (p.y - 1.3);
float katsura = udBox(p - float3(0.0, 1.3 + ty, 0.0), float3(1.7 + tx, r * 2.0 + ty, r));
float kan = udBox(p - float3(0.0, 0.7, 0.0), float3(1.3, r, r));
float gakuduka = udBox(p - float3(0.0, 1.0, 0.0), float3(r, 0.3, r));
return min(min(left, right), min(gakuduka, min(katsura, kan)));
}
float dRepGate(float3 p) {
if (normalizedGlobalTime <= 0.5) {
p.z = opRep(p.z, 1.0 + 20.0 * cos(PI * normalizedGlobalTime));
}
else {
p.xz = opRep(p.xz, 10.0);
}
return dGate(p);
}
float sceneDistance(float3 p) {
return udFloor(p);
}
// color functions
float3 hsv2rgb(float3 c) {
float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 p = abs(frac(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * lerp(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
Intersect minIntersect(Intersect a, Intersect b) {
if (a.distance < b.distance) {
return a;
}
else {
return b;
}
}
Intersect sceneIntersect(float3 p) {
Intersect a;
a.distance = udFloor(p);
a.material = MIRROR_MATERIAL;
// return minIntersect( a, b );
return a;
}
float3 getNormal(float3 p) {
float2 e = float2(1.0, -1.0) * 0.001;
return normalize(
e.xyy * sceneDistance(p + e.xyy) + e.yyx * sceneDistance(p + e.yyx) +
e.yxy * sceneDistance(p + e.yxy) + e.xxx * sceneDistance(p + e.xxx));
}
float getShadow(float3 ro, float3 rd) {
float h = 0.0;
float c = 0.0;
float r = 1.0;
float shadowCoef = 0.5;
for (float t = 0.0; t < 50.0; t++) {
h = sceneDistance(ro + rd * c);
if (h < EPS) return shadowCoef;
r = min(r, h * 16.0 / c);
c += h;
}
return 1.0 - shadowCoef + r * shadowCoef;
}
Intersect getRayColor(float3 origin, float3 ray) {
// marching loop
float dist, minDist, trueDepth;
float depth = 0.0;
float3 p = origin;
int count = 0;
Intersect nearest;
nearest.color = (0.0).xxx;
nearest.distance = 0.0;
// first pass (water)
for (int i = 0; i < 120; i++) {
dist = sceneDistance(p);
depth += dist;
p = origin + depth * ray;
count = i;
if (abs(dist) < EPS) break;
}
if (abs(dist) < EPS) {
nearest = sceneIntersect(p);
nearest.position = p;
nearest.normal = getNormal(p);
nearest.distance = depth;
float diffuse = clamp(dot(lightDir, nearest.normal), 0.1, 1.0);
float specular = pow(clamp(dot(reflect(lightDir, nearest.normal), ray), 0.0, 1.0), 6.0);
//float shadow = getShadow( p + nearest.normal * OFFSET, lightDir );
if (nearest.material == BASIC_MATERIAL) {
}
else if (nearest.material == MIRROR_MATERIAL) {
nearest.color = float3(0.5, 0.7, 0.8) * diffuse + (1.0).xxx * specular;
}
nearest.isHit = true;
}
else {
nearest.color = backgroundColor;
nearest.isHit = false;
}
nearest.color = clamp(nearest.color - 0.1 * nearest.distance, 0.0, 1.0);
// second pass (gates)
p = origin;
depth = 0.0;
minDist = INF;
for (int j = 0; j < 20; j++) {
dist = dRepGate(p);
minDist = min(dist, minDist);
/*if ( dist < minDist ) {
minDist = dist;
trueDepth = depth;
}*/
depth += dist;
p = origin + depth * ray;
if (j == 9 && normalizedGlobalTime <= 0.5) {
break;
}
}
if (abs(dist) < EPS) {
nearest.color += gateColor;
}
else {
nearest.color += gateColor * clamp(0.05 / minDist, 0.0, 1.0);
}
return nearest;
}
float4 FullScreenPass(Varyings varyings) : SV_Target
{
float depth = LoadCameraDepth(varyings.positionCS.xy);
PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
float4 color = float4(0.0, 0.0, 0.0, 0.0);
if (_CustomPassInjectionPoint != CUSTOMPASSINJECTIONPOINT_BEFORE_RENDERING)
color = float4(CustomPassSampleCameraColor(posInput.positionNDC.xy, 0), 1);
normalizedGlobalTime = mod(_Time.y / totalTime, 1.0);
// float2 uv = i.uv;
// float2 uv = posInput.positionNDC.xy;
// float2 p = 2.0 * i.uv - 1.0;
float2 p = (2.*varyings.positionCS.xy - _ScreenSize.xy) / min(_ScreenSize.x, _ScreenSize.y);
// float2 p = 2.*uv - 1.;
//float2 p = (fragCoord.xy * 2.0 - iResolution.xy) / min(iResolution.x, iResolution.y);
// camera and ray
if (normalizedGlobalTime < 0.7) {
cPos = float3(0.0, 0.6 + 0.4 * cos(_Time.y), 3.0 * _Time.y);
cDir = normalize(float3(0.0, -0.1, 1.0));
}
else {
cPos = float3(0.0, 0.6 + 0.4 * cos(_Time.y) + 50.0 * (normalizedGlobalTime - 0.7), 3.0 * _Time.y);
cDir = normalize(float3(0.0, -0.1 - (normalizedGlobalTime - 0.7), 1.0));
}
float3 cSide = normalize(cross(cDir, float3(0.0, 1.0, 0.0)));
float3 cUp = normalize(cross(cSide, cDir));
float targetDepth = 1.3;
float3 ray = normalize(cSide * p.x + cUp * p.y + cDir * targetDepth);
// Illumination col
// illuminationcol = hsv2rgb( float3( _Time.y * 0.02 + 0.6, 1.0, 1.0 ) );
float3 col = (0.0).xxx;
float alpha = 1.0;
Intersect nearest;
for (int i = 0; i < 3; i++) {
nearest = getRayColor(cPos, ray);
col += (alpha * nearest.color);
alpha *= 0.5;
ray = normalize(reflect(ray, nearest.normal));
cPos = nearest.position + nearest.normal * OFFSET;
if (!nearest.isHit || nearest.material != MIRROR_MATERIAL) break;
}
color.rgb = col;
return color;
}
ENDHLSL
SubShader
{
Pass
{
Name "Custom Pass4"
ZWrite Off
ZTest Always
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
HLSLPROGRAM
ENDHLSL
}
}
Fallback Off
}
https://gyazo.com/c32b125e5950ea3b5bf3cde5c2e6971d
code:tglads candy temple.HLSL
Shader "FullScreen/March6"
{
HLSLINCLUDE
#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl" const float EPS = .001;
float2 rot(float2 p, float a) {
return float2(p.x*cos(a) - p.y*sin(a), p.x*sin(a) + p.y*cos(a));
}
float2 pmod(float2 p, float m) {
float a = PI / m - atan2(p.y, p.x);
float r = 2.*PI / m;
a = floor(a / r)*r;
return rot(p, a);
}
float3 mod(float3 p, float a) { return p - floor(p / a)*a; }
float4 tglad(float3 p)
{
float3 z = float3(mod(p, 2.));
float3 q = float3(.0, 1.59, -1.);
float3 scale = (-acos(-1.)).xxx;
for (int i = 0; i < 3; ++i) {
z = clamp(z, -.94, .94)*2. - z;
z *= scale / clamp(dot(z, z), .25, 1.);
z += q;
}
return float4((length(max(abs(z) - float3(1.2, 49., 1.4), .0)) - .06) / pow(PI, 3. + EPS), z);
}
float4 map(float3 p) {
float3 q = p;
for (int i = 0; i < 3; ++i) {
q = abs(q) - 1.5;
q.xy = pmod(q.xy, 1.);
q.y -= .1;
q.yz = pmod(q.yz, -1.);
q *= .7;
}
float4 d = tglad(q);
return float4(d.x - .1, d.yzw);
}
float3 norm(float3 p) { float2 e = float2(.01, .0); return .000001 + map(p).x - float3(map(p - e.xyy).x, map(p - e.yxy).x, map(p - e.yyx).x); }
float4 FullScreenPass(Varyings varyings) : SV_Target
{
float depth = LoadCameraDepth(varyings.positionCS.xy);
PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
float4 color = float4(0.0, 0.0, 0.0, 0.0);
if (_CustomPassInjectionPoint != CUSTOMPASSINJECTIONPOINT_BEFORE_RENDERING)
color = float4(CustomPassSampleCameraColor(posInput.positionNDC.xy, 0), 1);
float2 p = (2.*varyings.positionCS.xy - _ScreenSize.xy) / min(_ScreenSize.x, _ScreenSize.y);
float3 col = (0.0).xxx;
float3 ro = float3(.5 - time, .0 + time, .0)
, ta = ro - float3(0., 0., 3.)
, up = float3(0., 1., .0);
ro.xy = rot(ro.xy, 1.);
ta.yz = rot(ta.yz, 1.);
float3 fwd = normalize(ta - ro)
, side = normalize(cross(up, fwd));
up = normalize(cross(fwd, side));
float3 rd = normalize(p.x*side + p.y*up + fwd);
float3 ray = ro, N;
for (int i = 0; i < 99; ++i) {
float d = map(ray).x;
if (d < EPS) { N = norm(ray); break; }
ray += d * rd;
}
col = frac(.075*map(ray).yzw + float3(.0, .6, .3));
col += dot(N,normalize(ta-ray));
col += map(ray + .01*rd).x*2.;
col += map(ray + .1*rd).x*4.;
color.rgb = col;
return color;
}
ENDHLSL
SubShader
{
Pass
{
Name "Custom Pass6"
ZWrite Off
ZTest Always
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
HLSLPROGRAM
ENDHLSL
}
}
Fallback Off
}