1
\$\begingroup\$

Please refer to this Answer.

DMGregory script is working for me, although I have a problem I can't fix because I don't talk shader :)

The tiling will be applied correctly on geometry aligned on XY plane BUT if you have an orthogonal plane (let's think a common room with 4 walls), the texture will stretch through the plane. Do you know how to fix that? I would like to apply world coord to texture for all 4 walls. (Note that the "plane" I am using is a simple quad made of 2 triangles.)

Here is a screenshot:

enter image description here

EDIT: After digging a while I found this guy here which did something similar.

My guess was to use the plane normal to set up the texture but, not knowing the syntax, the result was... well nothing :)

Here you can see the shader. Anyway, as pointed by DMGregory, it doesn't work for all wall angles. Notice the one in diagonal, shown in the picture.

Shader "Diffuse - Worldspace" 
{
  Properties 
  {
    _Color ("Main Color", Color) = (1,1,1,1)
    _MainTex ("Base (RGB)", 2D) = "white" {}
    _Scale ("Texture Scale", Float) = 1.0
  }

  SubShader 
  {
    Tags { "RenderType"="Opaque" }
    LOD 200

    CGPROGRAM
    #pragma surface surf Lambert

    sampler2D _MainTex;
    fixed4 _Color;
    float _Scale;

    struct Input
    {
      float3 worldNormal;
      float3 worldPos;
    };

    void surf (Input IN, inout SurfaceOutput o)
    {
      float2 UV;
      fixed4 c;

      if(abs(IN.worldNormal.x)>0.5)
      {
        UV = IN.worldPos.yz; // side
        c = tex2D(_MainTex, UV* _Scale); // use WALLSIDE texture
      }
      else if(abs(IN.worldNormal.z)>0.5)
      {
        UV = IN.worldPos.xy; // front
        c = tex2D(_MainTex, UV* _Scale); // use WALL texture
      }
      else
      {
        UV = IN.worldPos.xz; // top
        c = tex2D(_MainTex, UV* _Scale); // use FLR texture
      }

      o.Albedo = c.rgb * _Color;
    }
    ENDCG
  }

  Fallback "VertexLit"
}
\$\endgroup\$
  • \$\begingroup\$ I'll post a modified version of the shader for you later today if no one beats me to it. In the meantime, consider embedding a screenshot of the issue — this will make it clearer to folks other than you and me. ;) Also, can you confirm: you only need this on hard-edged surfaces aligned to orthogonal world axes? No curved or diagonal walls? And no floors/ceilings? (Handling every case generically leads to something called triplanar mapping, but if your uses are more limited we can use simpler & cheaper methods) \$\endgroup\$ – DMGregory Feb 1 '17 at 13:09
1
\$\begingroup\$

Thanks for your patience. Here's a way to get decent worldspace texturing on flat/hard-edged surfaces for walls & ramps, aligning the vertical axis of the texture with the world up direction:

Example of the shader tiling the texture evenly

This won't work for purely horizontal surfaces like floors & ceilings, where world "up" points into the surface, not along it, so we'll include a fallback alignment for those cases (say, aligning the texture vertical with world "north" instead)

Here since we're in 3D I'm assuming we want to use a lit Surface Shader, so starting with a new default Surface Shader we need to do two things:

  1. Find the #pragma surface line and add "vertex:vert" to the end of it, to say we want to use a custom vertex-modifying function named "vert"

  2. Write a vertex shader called "vert" that takes an inout appdata_full argument and modifies its texture coordinates.

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows vertex:vert
    
        void vert(inout appdata_full v) {
    
            // Get the worldspace normal after transformation, and ensure it's unit length.
            float3 n = normalize(mul(unity_ObjectToWorld, v.normal).xyz);
    
            // Pick a direction for our texture's vertical "v" axis. 
            // Default for floors/ceilings:
            float3 vDirection = float3(0, 0, 1);
    
            // For non-horizontal planes, we'll choose
            // the closest vector in the polygon's plane to world up.
            if(abs(n.y) < 1.0f) {
                 vDirection = normalize(float3(0, 1, 0) - n.y * n);
            }
    
            // Get the perpendicular in-plane vector to use as our "u" direction.
            float3 uDirection = normalize(cross(n, vDirection));
    
            // Get the position of the vertex in worldspace.
            float3 worldSpace = mul(unity_ObjectToWorld, v.vertex).xyz;
    
            // Project the worldspace position of the vertex into our texturing plane,
            // and use this result as the primary texture coordinate.
            v.texcoord.xy = float2(dot(worldSpace, uDirection), dot(worldSpace, vDirection));
        }
    
\$\endgroup\$
  • \$\begingroup\$ Thank you for the detailed explanation! How do you find the time to write all this stuff? :) \$\endgroup\$ – Chiodo Feb 3 '17 at 10:09
  • \$\begingroup\$ I tried you code but something wrong. Where am I suppose to attach the snippet? In your original shader or the shader I posted? Anyway, it doesn't work on both of them. What am I doing wrong? Cheers \$\endgroup\$ – Chiodo Feb 3 '17 at 11:39
  • \$\begingroup\$ Unfortunately, "it doesn't work" is never enough information to diagnose the specific problem. As you can see above, it works quite fine in all my tests, so I'll need more details about what's different in your situation. What are the symptoms? Is the material neon pink? Is it mapping the texture in a skewed way? (pictures are great) Is it generating a shader compiler error? (Full & exact text of the error helps) etc... As you can see in the snippet, this all follows immediately after the CGPROGRAM line in the standard surface shader you get from Create->Shader->Standard Surface Shader \$\endgroup\$ – DMGregory Feb 3 '17 at 12:00

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for?Browse other questions tagged or ask your own question.