Open18

シェーダーとRaytracingShaderについて

MakihiroMakihiro

レイトレーシングについての学びなど

MakihiroMakihiro

closesthitシェーダーはサーフェースシェーダー側に書く

MakihiroMakihiro

最初、「全部raytraceに書けばいいんやな!」という認識で、ヒット点のマテリアルパラメータを取得するのに詰まっていた

MakihiroMakihiro

サーフェースシェーダーでclosesthitシェーダーを実装して、その中で自分のパラメータを取得するのが正解

MakihiroMakihiro

closesthit内で法線を求める

Vertex FetchVertex(uint vertexIndex)
{
    Vertex v;
    v.normal = UnityRayTracingFetchVertexAttribute3(vertexIndex, kVertexAttributeNormal);
    return v;
}

Vertex InterpolateVertices(Vertex v0, Vertex v1, Vertex v2, float3 barycentrics)
{
    Vertex v;
    #define INTERPOLATE_ATTRIBUTE(attr) v.attr = v0.attr * barycentrics.x + v1.attr * barycentrics.y + v2.attr * barycentrics.z
    INTERPOLATE_ATTRIBUTE(normal);
    return v;
}
uint3 triangleIndices = UnityRayTracingFetchTriangleIndices(PrimitiveIndex());

Vertex v0, v1, v2;
v0 = FetchVertex(triangleIndices.x);
v1 = FetchVertex(triangleIndices.y);
v2 = FetchVertex(triangleIndices.z);

float3 barycentricCoords = float3(1.0 - attribs.barycentrics.x - attribs.barycentrics.y, attribs.barycentrics.x, attribs.barycentrics.y);
Vertex v = InterpolateVertices(v0, v1, v2, barycentricCoords);

float3 intersectionPosition = WorldRayOrigin() + RayTCurrent() * WorldRayDirection();

float3 worldNormal = normalize(mul(v.normal, (float3x3)WorldToObject()));
MakihiroMakihiro

レイを法線にしたがって法線を反射させる

float3 reflection = reflect(WorldRayDirection(), worldNormal);
MakihiroMakihiro

TraceRayは最終的に呼び出し元まで帰ってくる。
なので、最終的な出力はraygenerationシェーダー内で求めることが出来る

MakihiroMakihiro

参考ディスカッション
https://discussions.unity.com/t/raytracing-shader-how-does-closehit-shaders-works

通常のシェーダーに 1 つ以上のレイトレーシング パスを追加するだけです。通常のレンダリング中はレイトレーシング パスは無視されます。

RayTracingShader.SetShaderPass("pass name")でパスを追加することで、サーフェースシェーダー側のclosesthitシェーダーを認識できる

MakihiroMakihiro

レイトレーシングに関するシェーダーを読んでると出てくるShadowRayという概念は、どうやら「光がジオメトリによって遮られているかどうか」をチェックするためのレイのことのよう

MakihiroMakihiro

これはレイトレーシングシェーダーとは直接関係ないが、累積型のシェーディング処理について。
フレームごとに求められた照度を平均化する処理。

まずは入力にフレーム情報を持たせる

int _FrameIndex;

出力には累積用のバッファを持たせる

RWStructuredBuffer<float3> _AccumulatedBuffer;

現フレームでの値を求める

float3 frameAverage = frameValue / _SampleCountPerFrame;

塁積和を求める(累積 = 旧累積 + 新たな平均分)

float3 newAccumulated = _AccumulatedBuffer[0] + frameAverage;
_AccumulatedBuffer[0] = newAccumulated;

全フレームでの平均を求める(累積和 / フレーム数)

float3 finalAverage = newAccumulated / (float)(_FrameIndex + 1);
MakihiroMakihiro

レイのヒット点を求める基本的な公式

Origin + (Direction * RayTCurrent)

RayTCurrentはレイの長さ。OriginとDirectionはワールド空間またはオブジェクト空間のどちらかを扱うことができ、ワールド空間またはオブジェクト空間のレイのヒット点を求めることが出来る。

https://microsoft.github.io/DirectX-Specs/d3d/Raytracing.html#raytcurrent

MakihiroMakihiro

インクルードガードとかいうテクニックでincludeの再定義を防止できる

#ifndef XXX_INCLUDED
#define XXX_INCLUDED

#endif
MakihiroMakihiro

hlsl自体はコンパイルされず、shaderやraytraceにライブラリとして含めたものがコンパイルされるのね