Search Unity

How to use vertex texture fetch?

Discussion in 'Shaders' started by the_gnoblin, Oct 9, 2010.

  1. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    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!
     
  2. Aras

    Aras

    Unity Technologies

    Joined:
    Nov 7, 2005
    Posts:
    4,770
    Small except, to be put into a vertex shader:
    Code (csharp):
    1.  
    2. #if !defined(SHADER_API_OPENGL)
    3. float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));
    4. v.vertex.y += tex.r * 2.0;
    5. #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. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    Thank you!

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

    tokyob

    Joined:
    May 17, 2010
    Posts:
    37
    float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));

    look like it don't work into a surfaceShader vert function...
    i tryed this:
    Code (csharp):
    1. void vert(inout appdata_full v, out Input o) {
    2.     o.worldNormal= mul((float3x3)_Object2World, SCALED_NORMAL);
    3.     /* float4 o.vColor = [B]tex2Dlod[/B] (_map, float4(v.texcoord.xy,0,0)); */
    4.  
    5. };
    like that everything is OK, but if i un-comment the "tex2Dlod" thing , compiler return :
    Code (csharp):
    1. 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
     
  5. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    Is it possible to use it in a surface shader?

    Code (csharp):
    1. #pragma surface surf BlinnPhong vertex:vert
    2.  
    3. struct Input {
    4.     float2 uv_MainTex;
    5. };
    6.  
    7. void vert (inout appdata_full v) {
    8.             #if !defined(SHADER_API_OPENGL)
    9.             float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));
    10.             v.vertex.y += tex.r * 2.0;
    11.             #endif
    12.       }
    Shader error in ...: Program 'vert_surf', function "tex2Dlod" not supported in this profile at line 30

    thank you :)
     
  6. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    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
     
    Last edited: Nov 9, 2010
    brokenm likes this.
  7. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    dreamora, you are my personal hero!

    Indeed, it works with #pragma target 3.0 ;)
     
    TiG likes this.
  8. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    Cool :)
    looking forward to see what you do with it, love it when people to extravagant / more advanced stuff (ie overcome challenges)
     
  9. tokyob

    tokyob

    Joined:
    May 17, 2010
    Posts:
    37
    no matter what i try, " tex2Dlod" doesn't work...
    i used the code i found in this topic....

    Code (csharp):
    1. Shader "tex2Dlod" {
    2.     Properties {
    3.         _MainTex ("Base (RGB)", 2D) = "white" {}
    4.     }
    5.     SubShader {
    6.         Tags { "RenderType"="Opaque" }
    7.         LOD 200
    8.        
    9.         CGPROGRAM
    10.         #pragma target 3.0
    11.         #pragma surface surf BlinnPhong vertex:vert
    12.        
    13.  
    14.         struct Input {
    15.             float2 uv_MainTex;
    16.         };
    17.  
    18.         void vert (inout appdata_full v) {
    19.             #if !defined(SHADER_API_OPENGL)
    20.             float4 tex = tex2Dlod (_MainTex, float4(v.texcoord.xy,0,0));
    21.             v.vertex.y += tex.r * 2.0;
    22.             #endif
    23.         }      
    24.        
    25.         ENDCG
    26.     }
    27.     FallBack "Diffuse"
    28. }
    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 ?)
     
  10. the_gnoblin

    the_gnoblin

    Joined:
    Jan 10, 2009
    Posts:
    722
    well, that's because you need a surf function)

    something like this, for example:

     
  11. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    Note that although this error is exactly correct in this case, that same message often appears when there are other problems with your shader.
     
    PrimalCoder likes this.
  12. tokyob

    tokyob

    Joined:
    May 17, 2010
    Posts:
    37
    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 :)

    thank you !
     
    Last edited: Nov 26, 2010
  13. HonoraryBob

    HonoraryBob

    Joined:
    May 26, 2011
    Posts:
    1,214

    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?
     
  14. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    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):
    Code (csharp):
    1. // custom vertex function defined on the #pragma surface line
    2. void vert(inout appdata_full v) {
    3.     float4 tex = tex2Dlod(_MainTex, float4(v.texcoord.xy, 0, 0));
    4.     v.vertex.y += tex.r * 2.0;
    5. }
    6.  
    7. v2f_surf vert_surf (appdata_full v) { // vertex shader's actual main function
    8. {
    9.     v2f_surf o;
    10.     UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
    11.  
    12.     // call the custom vertex function passing in the appdata
    13.     vert(v);
    14.  
    15.     // assign the v2f struct used to actually transfer data between the vertex shader and fragment shader
    16.     o.pos = UnityObjectToClipPos(v.vertex);
    17.     o.pack0.xy = TRANSFORM_TEX(v.texcoord.xy, _MainTex); // uv_MainTex
    18.     return o;
    19. }
    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.
    Code (csharp):
    1. // custom vertex function defined on the #pragma surface line
    2. void vert(inout appdata_full v, out Input o) {
    3.     o.uv_MainTex = v.texcoord.xy * 2.0;
    4. }
    5.  
    6. v2f_surf vert_surf (appdata_full v) { // vertex shader's actual main function
    7. {
    8.     v2f_surf o;
    9.     UNITY_INITIALIZE_OUTPUT(v2f_surf,o);
    10.  
    11.     // create temporary Input struct
    12.     Input customInputData;
    13.     // call the custom vertex function passing in the appdata and the empty Input struct
    14.     vert(v, customInputData);
    15.  
    16.     // assign the v2f struct used to actually transfer data between the vertex shader and fragment shader
    17.     o.pos = UnityObjectToClipPos(v.vertex);
    18.     o.pack0.xy = TRANSFORM_TEX(v.texcoord.xy, _MainTex); // uv_MainTex, note the value assigned in the temp Input struct is ignored!
    19.     return o;
    20. }
    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.
     
    kadd11, zalogic, ScottJak and 7 others like this.