How to use vertex texture fetch?

Hello!

I’d like to access a texture in vertex shader. I’ve read in unity 3 release notes that it is now somehow possible.
Could anyone provide a small example?
thanks!

Small except, to be put into a vertex shader:

#if !defined(SHADER_API_OPENGL)
float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));
v.vertex.y += tex.r * 2.0;
#endif

This modifies vertex Y position based on red channel of _MainTex texture. As you can infer, this does not work on OpenGL by default. It should work on OpenGL if the shader is compiled into GLSL, I think (#pragma glsl).

3 Likes

Thank you!

Should I use one GLSL-compiled shader or somehow two separate versions?

float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));

look like it don’t work into a surfaceShader vert function…
i tryed this:

void vert(inout appdata_full v, out Input o) {
	o.worldNormal= mul((float3x3)_Object2World, SCALED_NORMAL);
	/* float4 o.vColor = [B]tex2Dlod[/B] (_map, float4(v.texcoord.xy,0,0)); */

};

like that everything is OK, but if i un-comment the “tex2Dlod” thing , compiler return :

Shader error in 'Mutsu/AO_SpecEnv': Surface shader function 'surf' not found

did i miss something ?
how can i do a texture lookup into surfaceShader vertex function ?

thank you

Is it possible to use it in a surface shader?

#pragma surface surf BlinnPhong vertex:vert

struct Input {
	float2 uv_MainTex;
};

void vert (inout appdata_full v) {
			#if !defined(SHADER_API_OPENGL)
			float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));
			v.vertex.y += tex.r * 2.0;
			#endif
      }

Shader error in …: Program ‘vert_surf’, function “tex2Dlod” not supported in this profile at line 30

thank you :slight_smile:

Just to be sure as its not mentioned: VTF requires SM 3.0, so check if the pragma is present for the shader model, also ensure to have above check / glsl enforcement

Also write a fallback without it, cause on ATI X1x00 which are technically marketed as SM3, VTF is not present

1 Like

dreamora, you are my personal hero!

Indeed, it works with #pragma target 3.0 :wink:

1 Like

Cool :slight_smile:
looking forward to see what you do with it, love it when people to extravagant / more advanced stuff (ie overcome challenges)

no matter what i try, " tex2Dlod" doesn’t work…
i used the code i found in this topic…

Shader "tex2Dlod" {
	Properties {
		_MainTex ("Base (RGB)", 2D) = "white" {}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		
		CGPROGRAM
		#pragma target 3.0
		#pragma surface surf BlinnPhong vertex:vert
		

		struct Input {
			float2 uv_MainTex;
		};

		void vert (inout appdata_full v) {
			#if !defined(SHADER_API_OPENGL)
			float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));
			v.vertex.y += tex.r * 2.0;
			#endif
		}		
		
		ENDCG
	} 
	FallBack "Diffuse"
}

but compiler return always the same error :
“Shader error in ‘tex2Dlod’: Surface shader function ‘surf’ not found at line 9”

can some one tell me where I miss something?

could that be a driver problem? (how is the nVidia GTX470 gpu supported in unity3.1 ?)

well, that’s because you need a surf function)

something like this, for example:

Note that although this error is exactly correct in this case, that same message often appears when there are other problems with your shader.

1 Like

OMG… i’m really sorry to be so blind…
please accept all my apologies for this ultra-nOOb mistake.

sure it really better with a surface function :slight_smile:

thank you !

I’ve noticed that everyone uses texcoord rather than uv_MainTex when accessing a texture from a vertex shader. Is texcoord always going to be the first item in the Input struct, or how is it assigned in a surface shader?

Serious thread necro … for anyone coming across this now, that #if !defined(SHADER_API_OPENGL) is out of date by almost a decade since all graphics APIs that Unity currently supports is able to use tex2Dlod in the vertex shader.

As for the question of why the vert function uses v.texcoord.xy instead of uv_MainTex, it’s because uv_MainTex doesn’t “exist” yet. In that particular form of the custom vertex function call, the Input struct isn’t even made available, but even if it was, the uv_MainTex value isn’t assigned until after that function is called. You can make modifications to the v.texcoord from that function and those changes will be reflected in uv_MainTex as that’s the vertex data used to generated the uv_MainTex later.

Basically the generated shader looks a little like this (greatly simplified):

// custom vertex function defined on the #pragma surface line
void vert(inout appdata_full v) {
    float4 tex = tex2Dlod(_MainTex, float4(v.texcoord.xy, 0, 0));
    v.vertex.y += tex.r * 2.0;
}

v2f_surf vert_surf (appdata_full v) { // vertex shader's actual main function
{
    v2f_surf o;
    UNITY_INITIALIZE_OUTPUT(v2f_surf,o);

    // call the custom vertex function passing in the appdata
    vert(v);

    // assign the v2f struct used to actually transfer data between the vertex shader and fragment shader
    o.pos = UnityObjectToClipPos(v.vertex);
    o.pack0.xy = TRANSFORM_TEX(v.texcoord.xy, _MainTex); // uv_MainTex
    return o;
}

Notice the Input struct never even exists. the “uv_MainTex” value gets generated and pass along as a custom packed part of the v2f struct. It’s not until the fragment shader that the Input struct gets created and uv_MainTex gets assigned to the texcoord calculated and stored in the v2f’s pack0.xy.

Alternatively, if you use the custom vertex function that takes the Input struct, the uv_MainTex value will still be assigned after the vert function is called, so it’ll be uninitialized in the vert function. Also if you assign uv_MainTex in the vert function it’ll get overridden later no matter what.

// custom vertex function defined on the #pragma surface line
void vert(inout appdata_full v, out Input o) {
    o.uv_MainTex = v.texcoord.xy * 2.0;
}

v2f_surf vert_surf (appdata_full v) { // vertex shader's actual main function
{
    v2f_surf o;
    UNITY_INITIALIZE_OUTPUT(v2f_surf,o);

    // create temporary Input struct
    Input customInputData;
    // call the custom vertex function passing in the appdata and the empty Input struct
    vert(v, customInputData);

    // assign the v2f struct used to actually transfer data between the vertex shader and fragment shader
    o.pos = UnityObjectToClipPos(v.vertex);
    o.pack0.xy = TRANSFORM_TEX(v.texcoord.xy, _MainTex); // uv_MainTex, note the value assigned in the temp Input struct is ignored!
    return o;
}

The only values copied out of the Input struct set by that vert function are those that aren’t special keywords. So anything that starts with uv, or any of the other specially handled Input struct values, will always be ignored and calculated / overridden by the surface shader.

11 Likes