Search Unity

  1. If you have experience with import & exporting custom (.unitypackage) packages, please help complete a survey (open until May 15, 2024).
    Dismiss Notice
  2. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice

Mapping texture to world position instead of object position?

Discussion in 'Shaders' started by cabot696, Jun 29, 2011.

  1. cabot696

    cabot696

    Joined:
    Jun 28, 2011
    Posts:
    3
    Hi, I'm trying to write a simple shader that maps a texture relative to world position rather than to individual uv-maps per object. I'm basically creating a procedural dungeon made out of tiles and instead of each tile having the same texture, it would be nice to spread a large texture across multiple tiles. I don't know much about cg/hlsl so I'm having a little trouble coming up with the solution.

    I'm using "float3 worldPos" as the input to world position but I don't know what to do with this. It seems that worldPos only maps to one side of the world so some other manipulation must be done to map the texture in 3d. How could I achieve this?

    My super simple shader looks like this so far

    "
    Code (csharp):
    1. Shader "Custom/World Map Simple" {
    2.     Properties {
    3.       _MainTex ("Texture", 2D) = "surface" {}
    4.     }
    5.     SubShader {
    6.       CGPROGRAM
    7.       #pragma surface surf Lambert
    8.       struct Input {
    9.           float3 worldPos;
    10.       };
    11.       sampler2D _MainTex;
    12.       void surf (Input IN, inout SurfaceOutput o) {
    13.         float3 worlduv = (IN.worldPos / 10);
    14.         o.Albedo = tex2D (_MainTex, worlduv).rgb;
    15.       }
    16.       ENDCG
    17.     }
    18.   }
    And this is what it does: http://i.imgur.com/DjzLL.jpg

    Thanks!
     
    Last edited: Jun 29, 2011
  2. cabot696

    cabot696

    Joined:
    Jun 28, 2011
    Posts:
    3
    Ah, that makes sense. How can I then combine together "worlduv.xy" and "worlduv.xz" together to have a fully textured environment?

    Code (csharp):
    1. o.Albedo = tex2D (_MainTex, worlduv.xy).rgb
    2. o.Albedo += tex2D (_Maintex, worlduv.xz).rgb
    Doesn't work exactly because each call of tex2D makes a mess of the wall that isn't mapped so adding them isn't a solution.

    Is there someway I can use tex3D() to texture the world because it takes a float3 as its coordinates? What about any other method? It seems to me that it shouldn't be too difficult to just map the world in 3d, I just don't know how to do it.
     
  3. Daniel_Brauer

    Daniel_Brauer

    Unity Technologies

    Joined:
    Aug 11, 2006
    Posts:
    3,355
    There is no way to seamlessly map a 2D texture to arbitrary 3D geometry. This is why UV mapping exists, and why it usually has to be done or at least guided by humans.

    If you don't mind some seams or fading, the simplest approach is called triplanar texturing. You can read about it in detail (including example code) in section 1.5 of this article from GPU Gems 3.
     
  4. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    This is ripped out of a shader I wrote for something else and quickly cobbled together for diffuse normal using worldspace UVs.

    It's a little shonky as it was built only to handle specular highlights, so it may not work (not at my PC to test it).

    Should do the trick, though.

    Code (csharp):
    1.  
    2. Shader "Bumped Diffuse WorldSpace"
    3. {
    4.  
    5.     Properties
    6.     {
    7.         _Color ("Main Color", Color) = (1,1,1,1)
    8.         _MainTex ("Diffuse (RGB)", 2D) = "white" {}
    9.         _BumpMap ("Normal (Normal)", 2D) = "bump" {}
    10.         _SpecularTex ("Specular Level (R) Gloss (G) Empty (B)", 2D) = "grey" {}
    11.     }
    12.    
    13.     SubShader
    14.     {
    15.         Pass
    16.         {
    17.             Tags {"LightMode" = "ForwardBase"}
    18.            
    19.             CGPROGRAM
    20.                 #pragma target 3.0
    21.                 #pragma vertex vert
    22.                 #pragma fragment frag
    23.                 #pragma multi_compile_fwdbase
    24.                 #pragma fragmentoption ARB_precision_hint_fastest
    25.                
    26.                 #include "UnityCG.cginc"
    27.                 #include "AutoLight.cginc"
    28.                
    29.                 struct v2f
    30.                 {
    31.                     float4  pos : SV_POSITION;
    32.                     float3  worldPos : TEXCOORD0;
    33.                     float3  worldNormal : TEXCOORD1;
    34.                     float3  lightDir : TEXCOORD2;
    35.                     float3  viewDir : TEXCOORD3;
    36.                     LIGHTING_COORDS(4,5)
    37.                 };
    38.  
    39.                 v2f vert (appdata_base v)
    40.                 {
    41.                     v2f o;
    42.                     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    43.                     o.worldPos = mul(_Object2World, v.vertex).xyz;
    44.                     o.worldNormal = mul(_Object2World, float4(v.normal, 0.0)).xyz;
    45.                     o.lightDir = normalize(ObjSpaceLightDir(v.vertex));
    46.                     o.viewDir = normalize(ObjSpaceViewDir(v.vertex));
    47.                    
    48.                     TRANSFER_VERTEX_TO_FRAGMENT(o);
    49.                     return o;
    50.                 }
    51.                
    52.                 sampler2D _MainTex, _BumpMap, _SpecularTex;
    53.                
    54.                 float4 _LightColor0;
    55.  
    56.                 half4 frag(v2f i) : COLOR
    57.                 {
    58.                         float3 worldNormal = normalize(i.worldNormal);
    59.                         float3 projNormal = saturate(pow(worldNormal*1.5, 4));
    60.  
    61.                     // CALCULATE TANGENT SPACE
    62.                        
    63.                         float4 tangent = float4(0);
    64.                         tangent.xyz = lerp(tangent.xyz, float3(0, 0, 1), projNormal.y);
    65.                         tangent.xyz = lerp(tangent.xyz, float3(0, 1, 0), projNormal.x);
    66.                         tangent.xyz = lerp(tangent.xyz, float3(1, 0, 0), projNormal.z);
    67.                         tangent.xyz = tangent.xyz - dot(tangent.xyz, worldNormal) * worldNormal;
    68.                         tangent.xyz = normalize(tangent.xyz);
    69.                        
    70.                         tangent.w = lerp(tangent.w, worldNormal.y, projNormal.y);
    71.                         tangent.w = lerp(tangent.w, -worldNormal.x, projNormal.x);
    72.                         tangent.w = lerp(tangent.w, worldNormal.z, projNormal.z);
    73.                         tangent.w = step(tangent.w, 0);
    74.                         tangent.w *= -2;
    75.                         tangent.w += 1;
    76.                        
    77.                         float3 binormal = cross(worldNormal, tangent.xyz) * tangent.w;
    78.                         float3x3 rotation = float3x3(tangent.xyz, binormal, worldNormal);
    79.                         float3 lightDirT = mul(rotation, i.lightDir);
    80.                         float3 viewDirT = mul(rotation, i.viewDir);
    81.  
    82.                     // TEXTURE INPUTS USING WORLD POSITION BASED UVS
    83.                         half3 albedo0 = tex2D(_MainTex, i.worldPos.xy).rgb;
    84.                         half3 albedo1 = tex2D(_MainTex, i.worldPos.zx).rgb;
    85.                         half3 albedo2 = tex2D(_MainTex, i.worldPos.zy).rgb;
    86.                        
    87.                         half4 normal0 = tex2D(_BumpMap, i.worldPos.xy);
    88.                         half4 normal1 = tex2D(_BumpMap, i.worldPos.zx);
    89.                         half4 normal2 = tex2D(_BumpMap, i.worldPos.zy);
    90.                        
    91.                         half2 specgloss0 = tex2D(_SpecularTex, i.worldPos.xy).rg;
    92.                         half2 specgloss1 = tex2D(_SpecularTex, i.worldPos.zx).rg;
    93.                         half2 specgloss2 = tex2D(_SpecularTex, i.worldPos.zy).rg;
    94.                        
    95.                     // BLEND TEXTURE INPUTS BASED ON WORLD NORMAL
    96.                         float3 albedo;
    97.                         albedo = lerp(albedo1, albedo0, projNormal.z);
    98.                         albedo = lerp(albedo, albedo2, projNormal.x);
    99.                        
    100.                         float2 specgloss;
    101.                         specgloss = lerp(specgloss1, specgloss0, projNormal.z);
    102.                         specgloss = lerp(specgloss, specgloss2, projNormal.x);
    103.                    
    104.                         float4 normal;
    105.                         normal = lerp(normal1, normal0, projNormal.z);
    106.                         normal = lerp(normal, normal2, projNormal.x);
    107.                         float3 finalNormal = UnpackNormal(normal);
    108.                    
    109.                     // LIGHTING TERMS
    110.                         float NdotL = saturate(dot(normal, lightDirT));
    111.                         float3 halfAngle = normalize(lightDirT + viewDirT);
    112.                         float atten = LIGHT_ATTENUATION(i);
    113.                         fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb * 2;
    114.                         half specularity = pow(saturate(dot(normal, halfAngle)), specgloss.g * 128) * specgloss.r;
    115.                        
    116.                     // RESULT
    117.                         half4 result;
    118.                         result.rgb = (ambient * albedo) + (albedo * _LightColor0.rgb * NdotL + _LightColor0.rgb * specularity) * (atten * 2);
    119.                         result.a = 1;
    120.                         return result;
    121.                 }
    122.             ENDCG
    123.         }
    124.  
    125.         Pass
    126.         {
    127.             Tags {"LightMode" = "ForwardAdd"}
    128.             Blend One One
    129.            
    130.             CGPROGRAM
    131.                 #pragma target 3.0
    132.                 #pragma vertex vert
    133.                 #pragma fragment frag
    134.                 #pragma multi_compile_fwdbase
    135.                 #pragma fragmentoption ARB_precision_hint_fastest
    136.                
    137.                 #include "UnityCG.cginc"
    138.                 #include "AutoLight.cginc"
    139.                
    140.                 struct v2f
    141.                 {
    142.                     float4  pos : SV_POSITION;
    143.                     float3  worldPos : TEXCOORD0;
    144.                     float3  worldNormal : TEXCOORD1;
    145.                     float3  lightDir : TEXCOORD2;
    146.                     float3  viewDir : TEXCOORD3;
    147.                     LIGHTING_COORDS(4,5)
    148.                 };
    149.  
    150.                 v2f vert (appdata_base v)
    151.                 {
    152.                     v2f o;
    153.                     o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    154.                     o.worldPos = mul(_Object2World, v.vertex).xyz;
    155.                     o.worldNormal = mul(_Object2World, float4(v.normal, 0.0)).xyz;
    156.                     o.lightDir = normalize(ObjSpaceLightDir(v.vertex));
    157.                     o.viewDir = normalize(ObjSpaceViewDir(v.vertex));
    158.                    
    159.                     TRANSFER_VERTEX_TO_FRAGMENT(o);
    160.                     return o;
    161.                 }
    162.                
    163.                 sampler2D _MainTex, _BumpMap, _SpecularTex;
    164.                
    165.                 float4 _LightColor0;
    166.  
    167.                 half4 frag(v2f i) : COLOR
    168.                 {
    169.                         float3 worldNormal = normalize(i.worldNormal);
    170.                         float3 projNormal = saturate(pow(worldNormal*1.5, 4));
    171.  
    172.                     // CALCULATE TANGENT SPACE
    173.                        
    174.                         float4 tangent = float4(0);
    175.                         tangent.xyz = lerp(tangent.xyz, float3(0, 0, 1), projNormal.y);
    176.                         tangent.xyz = lerp(tangent.xyz, float3(0, 1, 0), projNormal.x);
    177.                         tangent.xyz = lerp(tangent.xyz, float3(1, 0, 0), projNormal.z);
    178.                         tangent.xyz = tangent.xyz - dot(tangent.xyz, worldNormal) * worldNormal;
    179.                         tangent.xyz = normalize(tangent.xyz);
    180.                        
    181.                         tangent.w = lerp(tangent.w, worldNormal.y, projNormal.y);
    182.                         tangent.w = lerp(tangent.w, -worldNormal.x, projNormal.x);
    183.                         tangent.w = lerp(tangent.w, worldNormal.z, projNormal.z);
    184.                         tangent.w = step(tangent.w, 0);
    185.                         tangent.w *= -2;
    186.                         tangent.w += 1;
    187.                        
    188.                         float3 binormal = cross(worldNormal, tangent.xyz) * tangent.w;
    189.                         float3x3 rotation = float3x3(tangent.xyz, binormal, worldNormal);
    190.                         float3 lightDirT = mul(rotation, i.lightDir);
    191.                         float3 viewDirT = mul(rotation, i.viewDir);
    192.  
    193.                     // TEXTURE INPUTS USING WORLD POSITION BASED UVS
    194.                         half3 albedo0 = tex2D(_MainTex, i.worldPos.xy).rgb;
    195.                         half3 albedo1 = tex2D(_MainTex, i.worldPos.zx).rgb;
    196.                         half3 albedo2 = tex2D(_MainTex, i.worldPos.zy).rgb;
    197.                        
    198.                         half4 normal0 = tex2D(_BumpMap, i.worldPos.xy);
    199.                         half4 normal1 = tex2D(_BumpMap, i.worldPos.zx);
    200.                         half4 normal2 = tex2D(_BumpMap, i.worldPos.zy);
    201.                        
    202.                         half2 specgloss0 = tex2D(_SpecularTex, i.worldPos.xy).rg;
    203.                         half2 specgloss1 = tex2D(_SpecularTex, i.worldPos.zx).rg;
    204.                         half2 specgloss2 = tex2D(_SpecularTex, i.worldPos.zy).rg;
    205.                        
    206.                     // BLEND TEXTURE INPUTS BASED ON WORLD NORMAL
    207.                         float3 albedo;
    208.                         albedo = lerp(albedo1, albedo0, projNormal.z);
    209.                         albedo = lerp(albedo, albedo2, projNormal.x);
    210.                        
    211.                         float2 specgloss;
    212.                         specgloss = lerp(specgloss1, specgloss0, projNormal.z);
    213.                         specgloss = lerp(specgloss, specgloss2, projNormal.x);
    214.                    
    215.                         float4 normal;
    216.                         normal = lerp(normal1, normal0, projNormal.z);
    217.                         normal = lerp(normal, normal2, projNormal.x);
    218.                         float3 finalNormal = UnpackNormal(normal);
    219.                    
    220.                     // LIGHTING TERMS
    221.                         float NdotL = saturate(dot(normal, lightDirT));
    222.                         float3 halfAngle = normalize(lightDirT + viewDirT);
    223.                         float atten = LIGHT_ATTENUATION(i);
    224.                         half specularity = pow(saturate(dot(normal, halfAngle)), specgloss.g * 128) * specgloss.r;
    225.                        
    226.                     // RESULT
    227.                         half4 result;
    228.                         result.rgb = (albedo * _LightColor0.rgb * NdotL + _LightColor0.rgb * specularity) * (atten * 2);
    229.                         result.a = 1;
    230.                         return result;
    231.                 }
    232.             ENDCG
    233.         }
    234.     }
    235.     Fallback "VertexLit"
    236. }
    237.  
     
    Last edited: Jun 30, 2011
    NekrosArts and DireDay like this.
  5. cabot696

    cabot696

    Joined:
    Jun 28, 2011
    Posts:
    3
    Thanks a lot for the responses. Farfarer, when I paste your code into a new shader script, I get a bunch of different sort of errors. I guess I'll look into it more a little later but do you know why this is?

    Daniel, I'm reading through the GPU Gems 3 page that you sent me and it's been helpful so far but I'm unable to follow the example code along very well. It's using variables and functions that it never mentions ahead of time and it doesn't make much sense to me, especially this step in example 1-3.
    Code (csharp):
    1. float4 col1 = colorTex1.Sample(coord1);  
    2. float4 col2 = colorTex2.Sample(coord2);  
    3. float4 col3 = colorTex3.Sample(coord3);  
    What is this Sample() function? The cg tutorial on the Nvidia site doesn't mention it at all, it's not in the appendix, and Unity is giving me an error that says it doesn't exist. Any ideas?

    Thanks again.
     
  6. Farfarer

    Farfarer

    Joined:
    Aug 17, 2010
    Posts:
    2,249
    Sorry, updated my post with fixed code.
     
  7. Windcrosser

    Windcrosser

    Joined:
    Feb 3, 2018
    Posts:
    3
    Hey man,

    I actually bypassed the void vert() loop, and directly calculated in the viod surf loop, which works, and sexy AF,
    Also, it used the Unity PBR function library (ShandardSpecular / Standard), here's the code:

    P.S:

    also you can do Z alignment only, by assigning c3 to the o.Albedo

    and you can do XY Alignment only, by assigning c21 to the o.Albedo

    Code (CSharp):
    1. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
    2.  
    3. // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
    4.  
    5. // Basic Test for World Spece (World Aligned)
    6.  
    7. Shader "Custom/WS Base (m)" {
    8.     Properties {
    9.  
    10.         _Color ("Color", Color) = (1,1,1,1)
    11.         _MainTex ("Base Color", 2D) = "white" {}
    12.         _UVs ("UV Scale", float) = 1.0
    13.  
    14.     }
    15.  
    16.     SubShader {
    17.         Tags { "RenderType"="Opaque" }
    18.         LOD 300
    19.        
    20.         CGPROGRAM
    21.         // Physically based Standard lighting model, and enable shadows on all light types
    22.         #pragma surface surf StandardSpecular fullforwardshadows vertex:vert
    23.  
    24.         // Use shader model 3.0 target, to get nicer looking lighting
    25.         #pragma target 3.0
    26.  
    27.         #include "UnityCG.cginc"
    28.  
    29.         sampler2D _MainTex;
    30.         fixed4 _Color;
    31.         float _UVs;
    32. //        float3 worldPos;
    33. //        float3 worldNormal;
    34.  
    35.         struct Input {
    36.  
    37.                 float2 uv_MainTex;
    38.                 float3 worldPos;
    39.                 float3 worldNormal;
    40.  
    41.         };
    42.  
    43.  
    44.         //---- World Position Alignment Test ----
    45.  
    46.         void vert (inout appdata_full v) {
    47.  
    48.        //Texture Coordinates rotation and rescale
    49.  
    50.    
    51.         }
    52.  
    53.  
    54.         UNITY_INSTANCING_CBUFFER_START(Props)
    55.         UNITY_INSTANCING_CBUFFER_END
    56.  
    57.         void surf (Input IN, inout SurfaceOutputStandardSpecular o) {
    58.  
    59.  
    60.         //---- World Space (Aligned) Evaluation Here -----
    61.  
    62.             float3 Pos = IN.worldPos / (-1.0 * abs(_UVs) );
    63.  
    64.             float3 c00 = tex2D (_MainTex, IN.worldPos / 10);
    65.  
    66.             float3 c1 = tex2D ( _MainTex, Pos.yz ).rgb;
    67.             float3 c2 = tex2D ( _MainTex, Pos.xz ).rgb;
    68.             float3 c3 = tex2D ( _MainTex, Pos.xy ).rgb;
    69.  
    70.             float alpha21 = abs (IN.worldNormal.x);
    71.             float alpha23 = abs (IN.worldNormal.z);
    72.  
    73.             float3 c21 = lerp ( c2, c1, alpha21 ).rgb;
    74.             float3 c23 = lerp ( c21, c3, alpha23 ).rgb;
    75.  
    76.             //---- Base Color Adjustment -----
    77.  
    78.             fixed3 c = c23 * _Color;
    79.             o.Albedo = c23;
    80.  
    81.  
    82.         }
    83.         ENDCG
    84.     }
    85.     FallBack "Diffuse"
    86. }
    87.  
     
    tim88435 and SmashedBug like this.
  8. shough

    shough

    Joined:
    Sep 13, 2017
    Posts:
    5
    Just to add on to Windcrosser's great answer, if you want to do this with a different texture for walls, this works:
    Code (CSharp):
    1. Shader "Diffuse - Worldspace" {
    2. Properties {
    3.     _Color ("Main Color", Color) = (1,1,1,1)
    4.     _MainTex ("Base (RGB)", 2D) = "white" {}
    5.     _WallTex ("Base (RGB)", 2D) = "white" {}
    6.     _Scale ("Texture Scale", Float) = 1.0
    7. }
    8. SubShader {
    9.     Tags { "RenderType"="Opaque" }
    10.     LOD 200
    11.  
    12. CGPROGRAM
    13. #pragma surface surf Lambert
    14.  
    15. sampler2D _MainTex;
    16. sampler2D _WallTex;
    17. fixed4 _Color;
    18. float _Scale;
    19.  
    20. struct Input
    21. {
    22.     float3 worldNormal;
    23.     float3 worldPos;
    24. };
    25.  
    26. void surf (Input IN, inout SurfaceOutput o)
    27. {
    28.     float2 UV;
    29.     fixed4 c;
    30.  
    31.     if(abs(IN.worldNormal.x)>0.5)
    32.     {
    33.         UV = IN.worldPos.yz; // side
    34.         c = tex2D(_WallTex, UV* _Scale); // use WALLSIDE texture
    35.     }
    36.     else if(abs(IN.worldNormal.z)>0.5)
    37.     {
    38.         UV = IN.worldPos.xy; // front
    39.         c = tex2D(_WallTex, UV* _Scale); // use WALL texture
    40.     }
    41.     else
    42.     {
    43.         UV = IN.worldPos.xz; // top
    44.         c = tex2D(_MainTex, UV* _Scale); // use FLR texture
    45.     }
    46.  
    47.     o.Albedo = c.rgb * _Color;
    48. }
    49. ENDCG
    50. }
    51.  
    52. Fallback "VertexLit"
    53. }
    54.  
    Source: Breakfast with Unity -
     
    sama-van and zanouk like this.
  9. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    I always try to prevent if statements in a shader. (Since multiple parts get often executed anyway.) And that enables some blending:
    Code (csharp):
    1.  
    2. float3 uvs = IN.worldPos.xyz * _Scale;
    3. float3 blending = saturate(abs(IN.worldNormal.xyz) - 0.2); // Change the 0.2 value to adjust blending
    4. blending = pow(blending, 2.0); // Change the 2.0 value to adjust blending
    5. blending /= dot(blending, float3(1.0, 1.0, 1.0));
    6. float4 c = blending.x * tex2D(_WallTex, uvs.yz);
    7. c = blending.y * tex2D(_MainTex, uvs.xz) + c; // Single MAD
    8. c = blending.z * tex2D(_WallTex, uvs.xy) + c; // Single MAD
    9.  
    The single MAD comment means the notation order is in such a way it will optimized into a single MAD instruction.

    This is essentially a basic triplanar mapping shader with (adjustable) blending.
     
    geniusz, Zapan15 and IsDon like this.