Написать простой шейдер raycast
Я хочу сделать простой однопроходный шейдер raycast. То есть я передаю мировую позицию вершины в вершинном шейдере, а в фрагментном шейдере начинаю движение луча по оси Z (я использую ортопроекцию), чтобы получить положение образца в мировой координате, затем мне нужно вычислить мировую координату по координате модели, чтобы получить образец (текстура) положение в 3d текстуре, где я застрял в сейчас. Я сделал то же самое в cpp с opengl, я хочу попробовать это в unity3d и использовать преимущества графического интерфейса. Код здесь, но он не сработал. Спасибо за чью-либо помощь, и прошу прощения за мой плохой английский.
Shader "Customer/RayCast"
{
Properties
{
_Volume ("Volume (Scalar)", 3D) = "white"{}
//_VolumeGradient ("VolumeGradient (Scalar)", 3D) = "white"{}
//_TransferMap_Red ("TransferMap_Red", 2D) = "white"{}
//_TransferMap_Green ("TransferMap_Green", 2D) = "white"{}
//_TransferMap_Blue ("TransferMap_Blue", 2D) = "white"{}
//_TransferMap_Alpha ("TransferMap_Alpha", 2D) = "white"{}
_Slider("slider", Float) = 0.2
}
SubShader
{
Tags { "Queue" = "Transparent" }
Pass
{
Cull Back
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
// Upgrade NOTE: excluded shader from DX11 and Xbox360; has structs without semantics (struct v2f members eye_pos)
#pragma exclude_renderers d3d11 xbox360
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 pos : POSITION;
float3 world_pos;
};
v2f vert(appdata_base v)
{
v2f output;
output.pos = mul (UNITY_MATRIX_MVP, v.vertex);
output.world_pos = mul(_Object2World, v.vertex);
return output;
}
sampler3D _Volume;
float _Slider;
float4 frag( v2f input ) : COLOR
{
float3 start = input.world_pos;
float4 dst = float4(0.0, 0.0, 0.0, 0.0);//final color.
float3 ray_step = 0.01 * float3(0.0f, 0.0f, 1.0f);//camera look at positive z- axis with ortho projection.
float4 value;
float3 sample_pos = start;
float3 offset = float3(0.5f, 0.5f, 0.5f);//model vertex is -0.5 to 0.5, make it 0 to 1.0.
for(int i = 0; i < 100; ++i)
{
sample_pos += (i * ray_step);
value = tex3D(_Volume, mul(_World2Object, sample_pos).xyz + offset);
value.a = 0.1f;
value.xyz *= value.a;
dst += (1.0 - dst.a) * value;
}
dst += float4(0.5f, 0.0f, 0.0f, 0.0f);
return dst;
}
ENDCG
}
}
}
Еще один вопрос: #program target 3.0, кажется, не позволяет циклу for иметь более 300 раз, а в opengl я использую 500 без проблем с той же видеокартой. И #program target 4.0 не поддерживается, есть ли способ улучшить его в unity3d?
1 ответ
Спасибо за объяснение Джердака. Моя видеокарта поддерживает SM4.0, но я не могу набрать только #pragma target 4.0 в скрипте шейдера. из http://docs.unity3d.com/Documentation/Components/SL-ShaderPrograms.html говорится, что
pragma target 4.0 - компилировать в шейдерную модель DX10 4.0. В настоящее время эта цель поддерживается только рендерером DirectX 11.
Поэтому мне нужно поменять рендерер на dx11 в настройках Unity Player. Код ниже представляет собой простой однопроходный шейдер raycast с низкой эффективностью.
Shader "Customer/RayCast"
{
Properties{
_Volume ("Volume (Scalar)", 3D) = "white"{}
_TransferMap_Red ("TransferMap_Red", 2D) = "white"{}
_TransferMap_Green ("TransferMap_Green", 2D) = "white"{}
_TransferMap_Blue ("TransferMap_Blue", 2D) = "white"{}
_TransferMap_Alpha ("TransferMap_Alpha", 2D) = "white"{}
}
SubShader {
Tags { "Queue" = "Transparent" }
Pass {
Cull Back
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma target 4.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f
{
float4 pos : POSITION;
float3 world_pos : TEXCOORD;
};
v2f vert(appdata_base v)
{
v2f output;
output.pos = mul (UNITY_MATRIX_MVP, v.vertex);
output.world_pos = mul(_Object2World, v.vertex).xyz;
return output;
}
sampler3D _Volume;
sampler2D _TransferMap_Red;
sampler2D _TransferMap_Green;
sampler2D _TransferMap_Blue;
sampler2D _TransferMap_Alpha;
float4 frag( v2f input ) : COLOR
{
float3 offset = float3(0.5f, 0.5f, 0.5f);
float3 ray_step = 0.004f * float3(0.0f, 0.0f, 1.0f);//ortho camera look at positive z-axis.
float3 start = input.world_pos;
float4 dst = float4(0.0, 0.0, 0.0, 0.0);
float value;
float3 sample_pos = mul(_World2Object, float4(start.xyz, 1.0f));
float4 sampled_color = float4(0.0f, 0.0f, 0.0f, 0.0f);
for(int i = 0; i < 400; ++i)
{
float3 world_pos = start + (i * ray_step);
sample_pos = mul(_World2Object, float4(world_pos.xyz, 1.0f)).xyz + offset;//cube from unity is -0.5 to 0.5.
value = tex3D(_Volume, sample_pos).a;
if(0.0 < sample_pos.x && 1.0 > sample_pos.x && 0.0 < sample_pos.y && 1.0 > sample_pos.y && 0.0 < sample_pos.z && 1.0 > sample_pos.z)
{
if(value > 0.02 && dst.a < 0.98)
{
sampled_color.r = tex2D(_TransferMap_Red, float2(value, 0.5f)).a;
sampled_color.g = tex2D(_TransferMap_Green, float2(value, 0.5f)).a;
sampled_color.b = tex2D(_TransferMap_Blue, float2(value, 0.5f)).a;
sampled_color.a = tex2D(_TransferMap_Alpha, float2(value, 0.5f)).a;
dst = (sampled_color.a) * sampled_color + (1 - sampled_color.a) * dst;
}
}
}
if(dst.a < 0.05f)
dst.a = 0.0f;
return dst;
}
ENDCG
}
}
}