288 lines
11 KiB
C#
288 lines
11 KiB
C#
using System;
|
|
using System.Numerics;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using TerraFX.Interop.DirectX;
|
|
|
|
namespace Auspex.Rendering.Direct3D;
|
|
|
|
internal sealed class FanFillShader : IDisposable
|
|
{
|
|
public struct Constants
|
|
{
|
|
public Matrix4x4 ViewProj;
|
|
|
|
public Vector2 PixelToUv;
|
|
}
|
|
|
|
public struct Instance
|
|
{
|
|
public Vector3 Origin;
|
|
|
|
public float InnerRadius;
|
|
|
|
public float OuterRadius;
|
|
|
|
public float MinAngle;
|
|
|
|
public float MaxAngle;
|
|
|
|
public float NumSegments;
|
|
|
|
public Vector4 ColorOrigin;
|
|
|
|
public Vector4 ColorEnd;
|
|
|
|
public float OccludedAlpha;
|
|
|
|
public float OcclusionTolerance;
|
|
|
|
public float FadeStart;
|
|
|
|
public float FadeStop;
|
|
}
|
|
|
|
public sealed class Data : HlslShaderData<Instance>
|
|
{
|
|
public sealed class Builder : HlslShaderBuilder<Instance>
|
|
{
|
|
internal Builder(FrameRenderContext ctx, Data data)
|
|
: base(data._buffer.Map(ctx))
|
|
{
|
|
}
|
|
|
|
public void Add(Vector3 world, float innerRadius, float outerRadius, float minAngle, float maxAngle, Vector4 colorOrigin, Vector4 colorEnd, float numSegments, AxDxParams p)
|
|
{
|
|
_inner.Add(new Instance
|
|
{
|
|
Origin = world,
|
|
InnerRadius = innerRadius,
|
|
OuterRadius = outerRadius,
|
|
MinAngle = minAngle,
|
|
MaxAngle = maxAngle,
|
|
NumSegments = numSegments,
|
|
ColorOrigin = colorOrigin,
|
|
ColorEnd = colorEnd,
|
|
OccludedAlpha = p.OccludedAlpha,
|
|
OcclusionTolerance = p.OcclusionTolerance,
|
|
FadeStart = p.FadeStart,
|
|
FadeStop = p.FadeStop
|
|
});
|
|
}
|
|
}
|
|
|
|
public Data(FrameRenderContext ctx, int maxCount, bool dynamic)
|
|
: base("Fan", ctx, maxCount, dynamic)
|
|
{
|
|
}
|
|
|
|
public Builder Map(FrameRenderContext ctx)
|
|
{
|
|
return new Builder(ctx, this);
|
|
}
|
|
|
|
public unsafe void DrawAll(FrameRenderContext ctx)
|
|
{
|
|
ID3D11Buffer* buffer = _buffer.Buffer;
|
|
uint elementSize = (uint)_buffer.ElementSize;
|
|
uint num = 0u;
|
|
ctx.Context->IASetVertexBuffers(0u, 1u, &buffer, &elementSize, &num);
|
|
ctx.Context->DrawInstanced(722u, (uint)_buffer.CurElements, 0u, 0u);
|
|
}
|
|
}
|
|
|
|
private const int VerticesPerInstance = 722;
|
|
|
|
private unsafe ID3D11Buffer* _constantBuffer;
|
|
|
|
private unsafe ID3D11InputLayout* _il;
|
|
|
|
private unsafe ID3D11VertexShader* _vs;
|
|
|
|
private unsafe ID3D11PixelShader* _ps;
|
|
|
|
public unsafe FanFillShader(FrameRenderContext ctx)
|
|
{
|
|
byte[] bytes = Encoding.UTF8.GetBytes("#define PI 3.14159265359f\n#define MAX_SEGMENTS 360\n\ncbuffer Constants : register(b0)\n{\n float4x4 viewProj;\n float2 pixelToUv;\n};\n\nTexture2D<float4> _sceneDepth : register(t0);\nSamplerState _occlusionSampler\n{\n Filter = MIN_MAG_MIP_POINT;\n AddressU = CLAMP;\n AddressV = CLAMP;\n};\n\n// fadeParams: x=OccludedAlpha, y=OcclusionTolerance (m), z=FadeStart (m), w=FadeStop (m).\nfloat4 applyShared(float4 color, float3 projPos, float4 fadeParams)\n{\n float2 uv = projPos.xy * pixelToUv;\n float sceneNdcZ = _sceneDepth.Sample(_occlusionSampler, uv).r;\n\n float near = viewProj._m32;\n float shapeWorldZ = near / max(projPos.z, 1e-6);\n float sceneWorldZ = near / max(sceneNdcZ, 1e-6);\n\n float behindMeters = max(shapeWorldZ - sceneWorldZ, 0.0);\n float occlusion = behindMeters <= fadeParams.y ? 1.0 : fadeParams.x;\n\n float distanceFactor = 1.0;\n if (fadeParams.w < 1e10)\n {\n float range = max(fadeParams.w - fadeParams.z, 1e-4);\n distanceFactor = saturate((fadeParams.w - shapeWorldZ) / range);\n }\n\n color.a *= occlusion * distanceFactor;\n return color;\n}\n\nstruct Fan\n{\n float3 origin : WORLD;\n float innerRadius : RADIUS0;\n float outerRadius : RADIUS1;\n float minAngle : ANGLE0;\n float maxAngle : ANGLE1;\n float numSegments : NUMSEGMENTS;\n float4 colorOrigin : INSTANCECOLOR0;\n float4 colorEnd : INSTANCECOLOR1;\n float4 fadeParams : FADEPARAMS;\n};\n\nstruct VSOutput\n{\n float4 projPos : SV_POSITION;\n float4 color : COLOR;\n float2 tex : TEXCOORD;\n float4 fadeParams : FADEPARAMS;\n};\n\nVSOutput vs(in Fan instance, uint vertexId: SV_VERTEXID, uint instanceId: SV_INSTANCEID)\n{\n VSOutput o;\n\n uint segs = (uint)instance.numSegments;\n uint i = min(vertexId / 2, segs);\n\n float radius = 0;\n if (vertexId % 2 == 0) {\n o.color = instance.colorOrigin;\n o.tex.y = 0;\n radius = instance.innerRadius;\n } else {\n o.color = instance.colorEnd;\n o.tex.y = instance.outerRadius - instance.innerRadius;\n radius = instance.outerRadius;\n }\n float totalAngle = instance.maxAngle - instance.minAngle;\n float angleStep = totalAngle / instance.numSegments;\n float angle = PI / 2 + instance.minAngle + i * angleStep;\n float3 offset = radius * float3(cos(angle), 0, sin(angle));\n\n o.tex.x = angle;\n o.projPos = mul(float4(instance.origin + offset, 1.0), viewProj);\n o.fadeParams = instance.fadeParams;\n return o;\n}\n\nfloat4 ps(VSOutput input) : SV_TARGET\n{\n return applyShared(input.color, input.projPos.xyz, input.fadeParams);\n}");
|
|
TriangleFillShader.CompileShader(bytes, "vs"u8, "vs_5_0"u8, out var blob, "Circle VS");
|
|
TriangleFillShader.CompileShader(bytes, "ps"u8, "ps_5_0"u8, out var blob2, "Circle PS");
|
|
ID3D11VertexShader* vs = default(ID3D11VertexShader*);
|
|
Marshal.ThrowExceptionForHR(ctx.Device->CreateVertexShader(blob->GetBufferPointer(), blob->GetBufferSize(), null, &vs));
|
|
_vs = vs;
|
|
ID3D11PixelShader* ps = default(ID3D11PixelShader*);
|
|
Marshal.ThrowExceptionForHR(ctx.Device->CreatePixelShader(blob2->GetBufferPointer(), blob2->GetBufferSize(), null, &ps));
|
|
_ps = ps;
|
|
D3D11_BUFFER_DESC d3D11_BUFFER_DESC = new D3D11_BUFFER_DESC
|
|
{
|
|
ByteWidth = 80u,
|
|
Usage = D3D11_USAGE.D3D11_USAGE_DEFAULT,
|
|
BindFlags = 4u
|
|
};
|
|
ID3D11Buffer* constantBuffer = default(ID3D11Buffer*);
|
|
Marshal.ThrowExceptionForHR(ctx.Device->CreateBuffer(&d3D11_BUFFER_DESC, null, &constantBuffer));
|
|
_constantBuffer = constantBuffer;
|
|
fixed (byte* semanticName = "WORLD"u8)
|
|
{
|
|
fixed (byte* semanticName2 = "RADIUS"u8)
|
|
{
|
|
fixed (byte* semanticName3 = "ANGLE"u8)
|
|
{
|
|
fixed (byte* semanticName4 = "NUMSEGMENTS"u8)
|
|
{
|
|
fixed (byte* semanticName5 = "INSTANCECOLOR"u8)
|
|
{
|
|
fixed (byte* semanticName6 = "FADEPARAMS"u8)
|
|
{
|
|
D3D11_INPUT_ELEMENT_DESC* ptr = stackalloc D3D11_INPUT_ELEMENT_DESC[9];
|
|
*ptr = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
|
|
InstanceDataStepRate = 1u
|
|
};
|
|
ptr[1] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName2,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
|
|
InstanceDataStepRate = 1u
|
|
};
|
|
ptr[2] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName2,
|
|
SemanticIndex = 1u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
|
|
InstanceDataStepRate = 1u
|
|
};
|
|
ptr[3] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName3,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
|
|
InstanceDataStepRate = 1u
|
|
};
|
|
ptr[4] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName3,
|
|
SemanticIndex = 1u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
|
|
InstanceDataStepRate = 1u
|
|
};
|
|
ptr[5] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName4,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
|
|
InstanceDataStepRate = 1u
|
|
};
|
|
ptr[6] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName5,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
|
|
InstanceDataStepRate = 1u
|
|
};
|
|
ptr[7] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName5,
|
|
SemanticIndex = 1u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
|
|
InstanceDataStepRate = 1u
|
|
};
|
|
ptr[8] = new D3D11_INPUT_ELEMENT_DESC
|
|
{
|
|
SemanticName = (sbyte*)semanticName6,
|
|
SemanticIndex = 0u,
|
|
Format = DXGI_FORMAT.DXGI_FORMAT_R32G32B32A32_FLOAT,
|
|
AlignedByteOffset = uint.MaxValue,
|
|
InputSlot = 0u,
|
|
InputSlotClass = D3D11_INPUT_CLASSIFICATION.D3D11_INPUT_PER_INSTANCE_DATA,
|
|
InstanceDataStepRate = 1u
|
|
};
|
|
ID3D11InputLayout* il = default(ID3D11InputLayout*);
|
|
Marshal.ThrowExceptionForHR(ctx.Device->CreateInputLayout(ptr, 9u, blob->GetBufferPointer(), blob->GetBufferSize(), &il));
|
|
_il = il;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
blob->Release();
|
|
blob2->Release();
|
|
}
|
|
|
|
public unsafe void Dispose()
|
|
{
|
|
if (_constantBuffer != null)
|
|
{
|
|
_constantBuffer->Release();
|
|
_constantBuffer = null;
|
|
}
|
|
if (_il != null)
|
|
{
|
|
_il->Release();
|
|
_il = null;
|
|
}
|
|
if (_vs != null)
|
|
{
|
|
_vs->Release();
|
|
_vs = null;
|
|
}
|
|
if (_ps != null)
|
|
{
|
|
_ps->Release();
|
|
_ps = null;
|
|
}
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public unsafe void UpdateConstants(FrameRenderContext ctx, Constants consts)
|
|
{
|
|
consts.ViewProj = Matrix4x4.Transpose(consts.ViewProj);
|
|
ctx.Context->UpdateSubresource((ID3D11Resource*)_constantBuffer, 0u, null, &consts, 0u, 0u);
|
|
}
|
|
|
|
public unsafe void Bind(FrameRenderContext ctx)
|
|
{
|
|
ctx.Context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY.D3D_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP);
|
|
ctx.Context->IASetInputLayout(_il);
|
|
ctx.Context->VSSetShader(_vs, null, 0u);
|
|
ID3D11Buffer* constantBuffer = _constantBuffer;
|
|
ctx.Context->VSSetConstantBuffers(0u, 1u, &constantBuffer);
|
|
ctx.Context->PSSetShader(_ps, null, 0u);
|
|
ctx.Context->PSSetConstantBuffers(0u, 1u, &constantBuffer);
|
|
ctx.Context->GSSetShader(null, null, 0u);
|
|
}
|
|
|
|
public void Draw(FrameRenderContext ctx, Data data)
|
|
{
|
|
Bind(ctx);
|
|
data.DrawAll(ctx);
|
|
}
|
|
}
|