Hi,
I’m working on a water shader which has vertex displacement to create waves. When the waves are high enough the mesh behind can draw over the waves which are in front.
As far as I can tell, this is a complicated issue called “Order Independent Transparency”.
As I understand it, the shader doesn’t write to the z-buffer and the verts are rendered in the order that they are stored in the vertex array of the mesh. So the wave in front is drawn and then the wave behind overwrites that outpuf.
My best guess about how to solve this is to have the shader do the vertex displacement and then write to a custom z-buffer. Then I can do the actual surface shader part and only render the parts that are in front. This will mean that the transparency won’t be completely accurate because I wont be able to see the ocean through wave peaks but that’s probably fine.
I’m overwhelmed with the amount of information I’ve found and the sheer range of different possible solutions and I really need help discussing this problem within the specific context of what I’m working on so that I can understand what is happening and what my options are.
Here is my shader:
Shader "Custom/Ocean"
{
Properties
{
_WaterColour ("Water Colour", Color) = (0, 0, 0, 0)
_WaterFogColourLow ("Water Fog Colour Low", Color) = (0, 0, 0, 0)
_WaterFogColourHigh ("Water Fog Colour High", Color) = (0, 0, 0, 0)
_WaterMaxHeight ("Water High Colour End", float) = 10
_WaterFogDensity ("Water Fog Density", Range(0, 2)) = 0.1
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_AtmosphereColour ("Atmosphere Colour", Color) = (0, 0, 0, 0)
}
SubShader
{
Tags { "RenderType" = "Transparent" "Queue" = "Transparent"}
Cull OFF
GrabPass { "_WaterBackground" }
CGPROGRAM
#include "WaterShaderUtils.cginc"
#include "CustomFog.cginc"
#pragma surface surf Standard alpha vertex:vert finalcolor:FinalColour
#pragma target 5.0
#define TWO_PI 2 * UNITY_PI
struct Input
{
float2 uv_MainTex;
float4 screenPos;
float3 worldPos;
};
float4 _WaterColour;
half _Glossiness;
half _Metallic;
fixed4 _Color;
float _EditorTime;
float3 _PositionOffset;
float4 _AtmosphereColour;
float _FullAtmosphereDepth;
struct LinearWave
{
float Amplitude;
float Wavelength;
float Frequency;
float2 Direction;
};
#ifdef SHADER_API_D3D11
StructuredBuffer<LinearWave> _LinearWaveBuffer;
int _NumLinearWaves;
#endif
float3 GetDisplacementAtPosition(float3 p, LinearWave wave, inout float3 ddX, inout float3 ddZ)
{
float2 d = wave.Direction;
float a = wave.Amplitude;
float l = wave.Wavelength;
float f = wave.Frequency;
float pd = dot(p.xz, d);
float theta = (TWO_PI * pd / l) - (f * _EditorTime);
float k = (a * TWO_PI / l);
float sinTheta = sin(theta);
float cosTheta = cos(theta);
float3 vec = float3(-sinTheta * d.x, cosTheta, -sinTheta * d.y);
ddX += k * d.x * vec;
ddZ += k * d.y * vec;
return a * float3(cosTheta * d.x, sinTheta, sinTheta * d.y);
}
void vert (inout appdata_full v)
{
#ifdef SHADER_API_D3D11
float3 ddX = float3(0, 0, 0);
float3 ddZ = float3(0, 0, 0);
float4 vertexWorld = mul(unity_ObjectToWorld, v.vertex);
for (int i = 0; i < _NumLinearWaves; i++)
{
v.vertex.xyz += GetDisplacementAtPosition(
vertexWorld.xyz + _PositionOffset,
_LinearWaveBuffer[i],
ddX,
ddZ);
}
float3 tangent = normalize(float3(1, 0, 0) + ddX);
float3 biTangent = normalize(float3(0, 0, 1) + ddZ);
v.normal = normalize(cross(tangent, -biTangent));
#endif
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Albedo = _WaterColour.rgb;
o.Alpha = _WaterColour.a;
float3 emissionCol = ColorBelowWater(IN.screenPos, IN.worldPos) * (1 - _WaterColour.a);
o.Emission = emissionCol;
}
void FinalColour (Input IN, SurfaceOutputStandard o, inout fixed4 colour)
{
float distance = length(_WorldSpaceCameraPos - IN.worldPos);
float depth = distance / _ProjectionParams.z;
colour.rgb = lerp(colour.rgb, _AtmosphereColour, saturate(depth / _FullAtmosphereDepth));
float3 fogColour = LerpToFog(colour.rgb, distance);
colour.rgb = fogColour;
colour.a = 1;
}
ENDCG
}
}