Unity / HLSL:根据表面法线尝试使用纹理图集的不同部分时,结果为奇数

问题描述 投票:0回答:1

我是3D代码和编写着色器的新手,所以我可能在这里做一些愚蠢的事情。我正在研究一个简单的Minecraft副本,只是为了学习Unity,我认为最简单的对地面进行纹理处理的方法是使用带有草+尘土纹理的纹理地图集,并使用着色器选择要获取地图集的哪一部分取决于表面法线的纹理。即,对于一个指向上方的立方体表面,将使用草纹理,对于所有其他面孔,将使用污垢纹理。纹理看起来像这样:

Grass/dirt texture

在我的着色器中,如果执行以下操作,我将按预期得到每个面上的污垢纹理:

float y = (IN.uv_MainTex.y / 2.0f);
float2 uv = {IN.uv_MainTex.x, y};
fixed4 c = tex2D (_MainTex, uv) * _Color;
o.Albedo = c.rgb;

Cube with dirt texture

[如果我使用相同的代码,但始终向y添加0.5f以获得上半部分,即float y = (IN.uv_MainTex.y / 2.0f) + 0.5f;,则可以按预期获得到处的草纹理:

Cube with grass texture

但是当我尝试基于法线设置y时,即float y = (IN.uv_MainTex.y / 2.0f) + floor(IN.worldNormal.y) * 0.5f;,我得到了一个奇怪的结果,其中顶面主要是草皮,但部分污垢纹理以对角线显示:

Cube with grass/dirt texture showing distortion on top face

IN.worldNormal是否正确使用了法线,还是需要将其转换为其他空间?还是我如何使用floor()的问题?任何建议表示赞赏。

完整的着色器代码:

Shader "Custom/GroundShader"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
            float3 worldNormal;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            float y = (IN.uv_MainTex.y / 2.0f) + floor(IN.worldNormal.y) * 0.5f;

            float2 uv = {IN.uv_MainTex.x, y};

            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, uv) * _Color;

            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}
unity3d graphics shader hlsl
1个回答
0
投票

在Unity论坛上复制从Namey5获得的答案:

这似乎是一个精度问题(可能来自顶点法线插值)。可能有更聪明的方法可以做到这一点,但是简单的修复方法,您可以仅对正常值添加一点偏差,即;

float y = (IN.uv_MainTex.y / 2.0f) + floor (IN.worldNormal.y + 0.01) * 0.5f;

我将其略微修改为float y = (IN.uv_MainTex.y / 2.0f) + floor (IN.worldNormal.y * 1.1f) * 0.5f;,这可确保底面也显示出污垢纹理,因为法线将降低到-2而不是-1。

编辑:看起来更好的方法只是使用Mesh.uv属性而不是使用自定义着色器设置网格的UV。

© www.soinside.com 2019 - 2024. All rights reserved.