Example.
Source:
CS script:
using UnityEngine;
using UnityEngine.Rendering;
public class VolumeRenderTexture : MonoBehaviour
{
public ComputeShader VolumeShader;
public Material VolumeMaterial;
RenderTexture VRTA, VRTB;
float cx, cy, cz;
bool swap = true;
void Start()
{
RenderTextureDescriptor RTD = new RenderTextureDescriptor(256, 256, RenderTextureFormat.ARGB32);
RTD.dimension = TextureDimension.Tex3D;
RTD.volumeDepth = 256;
VRTA = new RenderTexture(RTD);
VRTA.enableRandomWrite = true;
VRTA.Create();
VRTB = new RenderTexture(RTD);
VRTB.enableRandomWrite = true;
VRTB.Create();
cx = cy = cz = 0.5f;
}
void Update()
{
if (Input.GetKey(KeyCode.W)) cy+=0.001f;
if (Input.GetKey(KeyCode.S)) cy-=0.001f;
if (Input.GetKey(KeyCode.A)) cx+=0.001f;
if (Input.GetKey(KeyCode.D)) cx-=0.001f;
if (Input.GetKey(KeyCode.Q)) cz+=0.001f;
if (Input.GetKey(KeyCode.E)) cz-=0.001f;
VolumeShader.SetFloats("Center",new float[3] {cx, cy, cz});
if (swap)
{
VolumeShader.SetTexture(0, "VolumeReader", VRTA);
VolumeShader.SetTexture(0, "VolumeWriter", VRTB);
VolumeShader.Dispatch(0, 256 / 8, 256 / 8, 256 / 8);
}
else
{
VolumeShader.SetTexture(0, "VolumeReader", VRTB);
VolumeShader.SetTexture(0, "VolumeWriter", VRTA);
VolumeShader.Dispatch(0, 256 / 8, 256 / 8, 256 / 8);
}
swap = !swap;
VolumeMaterial.SetTexture("_Volume",VRTB);
}
}
Compute shader to generate data:
#pragma kernel CSMain
Texture3D<float4> VolumeReader;
RWTexture3D<float4> VolumeWriter;
SamplerState _PointClamp;
float3 Center;
float circle(float3 p, float3 c, float r)
{
return step(length(p-c)-r,0.0);
}
[numthreads(8,8,8)]
void CSMain (uint3 id : SV_DispatchThreadID)
{
float3 uv = float3((float)id.x/256.0,(float)id.y/256.0,(float)id.z/256.0);
float k = circle(uv,Center,0.03) + VolumeReader.SampleLevel(_PointClamp,uv,0);
VolumeWriter[id] = float4(k,k,k,k);
}
Shader to render 3D texture
Shader "Volume Render Texture"
{
SubShader
{
Cull Back
Pass
{
CGPROGRAM
#pragma vertex VSMain
#pragma fragment PSMain
#pragma target 5.0
sampler3D _Volume;
void VSMain(inout float4 vertex:POSITION, out float3 world:WORLD)
{
world = mul(unity_ObjectToWorld, vertex).xyz;
vertex = UnityObjectToClipPos(vertex);
}
float4 PSMain(float4 vertex:POSITION, float3 world:WORLD) : SV_Target
{
float3 ro = mul(unity_WorldToObject, float4(world, 1)).xyz;
float3 rd = normalize(mul((float3x3) unity_WorldToObject, normalize(world - _WorldSpaceCameraPos)));
float3 tbot = (1.0 / rd) * (-0.5 - ro);
float3 ttop = (1.0 / rd) * (0.5 - ro);
float3 tmin = min(ttop, tbot);
float3 tmax = max(ttop, tbot);
float2 a = max(tmin.xx, tmin.yz);
float tnear = max(0.0,max(a.x, a.y));
float2 b = min(tmax.xx, tmax.yz);
float tfar = min(b.x, b.y);
float3 d = normalize((ro + rd * tfar) - ro) * (abs(tfar - tnear) / 128.0);
float4 t = float4(0, 0, 0, 0);
[unroll]
for (int i = 0; i < 128; i++)
{
float v = tex3D(_Volume, ro+0.5).r;
float4 s = float4(v, v, v, v);
s.a *= 0.5;
s.rgb *= s.a;
t = (1.0 - t.a) * s + t;
ro += d;
if (t.a > 0.99) break;
}
return saturate(t);
}
ENDCG
}
}
}
Example configuration:
I use voxel sphere as brush to “sculpt” virtual geometry inside 3D render texture. 256x256x256 and fps is still high.
Key configuration inside CS script.