Difficulty sampling texture from shader (float3) in C#, not possible?

Hi. I have a water shader that is offsetting vertices through the use of a heightmap, that is modified in the shader (things like panning and blending two heightmap textures at different angles to make convincing water).

I’d like to sample the final heightmap after it’s been modified in the shader, to offset the positions of GameObjects “bobbing” on the water.

I used Shaderforge to create this shader so I’m totally unfamiliar with shader code. I need to know the method of getting at a texture inside a shader (it seems like I need to basically export a float3?) and making use of it’s information in a C# script. It seems like I could use Texture2D.GetPixel to sample the texture, but I still don’t understand the syntax required to pass a texture from a shader to a Texture2D.

I’ve posted the relevant shader code that was created by Shaderforge, but there is no problem with this code, I just need to be able to sample “float3 node_6671” in a C# script.

VertexOutput vert (VertexInput v) {
VertexOutput o = (VertexOutput)0;
float2 node_6534 = mul(unity_ObjectToWorld, v.vertex).rgb.rb;
float4 node_5840 = _Time + _TimeEditor;
float2 node_8134 = ((node_6534float2(0.05,0.05))+(float2(2,2)node_5840.r));
float4 _hWave_var = tex2Dlod(_hWave,float4(TRANSFORM_TEX(node_8134, _hWave),0.0,0));
float4 node_3557 = _Time + _TimeEditor;
float2 node_2248 = ((node_6534
float2(0.02,0.02))+(float2(0.5,-3)node_5840.r));
float4 _node_4691_copy_copy = tex2Dlod(_hPerlin,float4(TRANSFORM_TEX(node_2248, _hPerlin),0.0,0));
float2 node_9998 = ((node_6534
float2(0.02,0.02))+(float2(1.5,-0.1)node_5840.r));
float4 _node_4691_copy_copy_copy = tex2Dlod(_hPerlin,float4(TRANSFORM_TEX(node_9998, _hPerlin),0.0,0));
float3 node_6671 = float3(float2(0,0),lerp(lerp(_hWave_var.b,(1.0 - _hWave_var.b),(0.2
(1.0+sin((4.0
node_3557.g))))),(0.6*(_node_4691_copy_copy.b*_node_4691_copy_copy_copy.b)),0.6));
//some way to make node_6671 public?
v.vertex.xyz += node_6671;
o.posWorld = mul(unity_ObjectToWorld, v.vertex);
o.pos = mul(UNITY_MATRIX_MVP, v.vertex );
TRANSFER_SHADOW_CASTER(o)
return o;
}

Please let me know if this is possible, or if there is a better way to do this. Thanks in advance.

Nope.

Generally speaking you cannot get data out of a shader like that. Shaders work by feeding data into it, like textures, material parameters, matrices, and a mesh, and it produces results in the form of a texture. All of that data, and the shader code, is handed over to the GPU and it does all of the work. Usually the output texture is the frame buffer, ie: it renders something onto the screen.

To do what you want you would need to make your shader and mesh render to a render texture. You would also need to have the vertex shader output a o.pos so that with in that render texture such that each vertex gets its own pixel to render to rather than using the vertex’s position. If you want to see what that looks like, you can look at Keijiro’s Skinner asset here: https://github.com/keijiro/Skinner

Then you would need to read that render texture back from the GPU to the CPU which can be quite slow, ie: this might be 20+ ms on it’s own.

The TLDR version is data is sent from the CPU (C#) to the GPU to be displayed or used on the GPU. You almost never want to try to get data off of the GPU back to the CPU. You’re far better off just redoing the same (or similar) calculations on the CPU than trying to reuse data from the GPU.

1 Like

Thanks so much for the detailed response bgolus. As this effect wasn’t necessary we’ve decided to scrap it and fake it with an animation. Still I really appreciate the explanation of shader data and how the GPU and CPU are working together, and your render texture work around. Cheers!