Начал изучать как в триплинарном шейдере реализовать карту адекватную нормалей, и наткнулся на статью по этой теме:
https://skine.ru/articles/467963/?ysclid=lpsb39xzk...
https://github.com/bgolus/Normal-Mapping-for-a-Tri...
Переписав шейдер под свои нужды сразу столкнулся с проблемой, на процедурном меше правая и левая стороны рисуются без применения карты нормалей. Сначала думал что где-то у меня ошибка и начал проверять исходник шейдера, он оказался с той же проблемой, при том что на примитиве или любой модели такой проблемы нет.
Сверху примитив куба, снизу созданный меш
Код шейдера:
Shader "Custom/Triplanar_Surface_Shader"
{
Properties
{
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_TextureColor ("Texture Color", Color) = (1, 1, 1, 1)
_OffsetAndTiling("Offset And Tiling", Vector) = (0, 0, 0, 1)
[Space]
[NoScaleOffset] _NormalMap("Normal Map", 2D) = "bump" {}
_Normal("Normal", Range(-5.0, 5.0)) = 1.0
[Space]
[Gamma] _Metallic("Metallic", Range(0, 1)) = 0
_Glossiness("Smoothness", Range(0, 1)) = 0.5
[Space]
[NoScaleOffset] _OcclusionMap("Occlusion", 2D) = "white" {}
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
_Test("Test", Range(0, 3)) = 3
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
#pragma surface surf Standard fullforwardshadows
#pragma target 3.0
#include "UnityStandardUtils.cginc"
// flip UVs horizontally to correct for back side projection
#define TRIPLANAR_CORRECT_PROJECTED_U
// offset UVs to prevent obvious mirroring
#define TRIPLANAR_UV_OFFSET
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _OffsetAndTiling;
sampler2D _NormalMap;
half _Normal;
half _Glossiness;
half _Metallic;
sampler2D _OcclusionMap;
half _OcclusionStrength;
int _Test;
struct Input {
float3 worldPos;
float3 worldNormal;
INTERNAL_DATA
};
// Altered to take normals (-1 to 1 ranges) rather than unsigned normal maps (0 to 1 ranges)
half3 blend_rnm(half3 n1, half3 n2)
{
n1.z += 1;
n2.xy = -n2.xy;
return n1 * dot(n1, n2) / n1.z - n2;
}
float3 WorldToTangentNormalVector(Input IN, float3 normal)
{
float3 t2w0 = WorldNormalVector(IN, float3(1,0,0));
float3 t2w1 = WorldNormalVector(IN, float3(0,1,0));
float3 t2w2 = WorldNormalVector(IN, float3(0,0,1));
float3x3 t2w = float3x3(t2w0, t2w1, t2w2);
return normalize(mul(t2w, normal));
}
void surf (Input IN, inout SurfaceOutputStandard o)
{
IN.worldPos = (IN.worldPos.xyz * _OffsetAndTiling.w) + _OffsetAndTiling.xyz;
// work around bug where IN.worldNormal is always (0, 0, 0)!
IN.worldNormal = WorldNormalVector(IN, float3(0, 0, 1));
// calculate triplanar blend
half3 triblend = saturate(pow(IN.worldNormal, 4));
triblend /= max(dot(triblend, half3(1,1,1)), 0.0001);
// calculate triplanar uvs applying texture scale and offset values ala TRANSFORM_TEX macro
float2 uvX = IN.worldPos.zy * _MainTex_ST.xy + _MainTex_ST.zy;
float2 uvY = -IN.worldPos.xz * _MainTex_ST.xy + _MainTex_ST.zy;
float2 uvZ = IN.worldPos.xy * _MainTex_ST.xy + _MainTex_ST.zy;
// offset UVs to prevent obvious mirroring
#if defined(TRIPLANAR_UV_OFFSET)
uvY += 0.33;
uvZ += 0.67;
#endif
// minor optimization of sign(). prevents return value of 0
half3 axisSign = IN.worldNormal < 0 ? -1 : 1;
// flip UVs horizontally to correct for back side projection
#if defined(TRIPLANAR_CORRECT_PROJECTED_U)
uvX.x *= axisSign.x;
uvY.x *= axisSign.y;
uvZ.x *= -axisSign.z;
#endif
/// *** Right nd Left side *** ///
fixed3 colX = tex2D(_MainTex, uvX).rgb * triblend.x;
half3 normX = UnpackNormal( tex2D (_NormalMap, uvX));
half occX = tex2D(_OcclusionMap, uvX).g * triblend.x;
/// *** Up and Down side *** ///
fixed3 colY = tex2D(_MainTex, uvY).rgb * triblend.y;
half3 normY = UnpackNormal(tex2D(_NormalMap, uvY));
half occY = tex2D(_OcclusionMap, uvY).g * triblend.y;
/// *** Forward and Back side *** ///
fixed3 colZ = tex2D(_MainTex, uvZ).rgb * triblend.z;
half3 normZ = UnpackNormal( tex2D (_NormalMap, uvZ));
half occZ = tex2D(_OcclusionMap, uvZ).g * triblend.z;
/// *** *** ///
fixed3 col = colX + colY + colZ;
half occ = LerpOneTo(occX + occY + occZ, _OcclusionStrength);
// flip normal maps' x axis to account for flipped UVs
#if defined(TRIPLANAR_CORRECT_PROJECTED_U)
normX.x *= axisSign.x;
normY.x *= axisSign.y;
normZ.x *= -axisSign.z;
#endif
half3 absVertNormal = abs(IN.worldNormal);
// swizzle world normals to match tangent space and apply reoriented normal mapping blend
normX = blend_rnm(half3(IN.worldNormal.zy, absVertNormal.x), normX);
normY = blend_rnm(half3(IN.worldNormal.xz, absVertNormal.y), normY);
normZ = blend_rnm(half3(IN.worldNormal.xy, absVertNormal.z), normZ);
// apply world space sign to tangent space Z
normX.z *= axisSign.x;
normY.z *= axisSign.y;
normZ.z *= axisSign.z;
// sizzle tangent normals to match world normal and blend together
half3 worldNormal = normalize(
(normX.zyx * triblend.x) +
(normY.xzy * triblend.y) +
(normZ.xyz * triblend.z)
);
if (_Test < 1)
{
o.Albedo = float3 (0.5, 0.5, 0.5);
o.Normal = WorldToTangentNormalVector(IN, worldNormal) * half4 (_Normal, _Normal, 1, 1);
} else
if (_Test >= 1 && _Test < 2)
{
o.Albedo = fixed3 (triblend.x, triblend.y, triblend.z);
} else {
// set surface ouput properties
o.Albedo = col.rgb;
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Occlusion = occ;
// convert world space normals into tangent normals
o.Normal = WorldToTangentNormalVector(IN, worldNormal) * half4 (_Normal, _Normal, 1, 1);
}
}
ENDCG
}
FallBack "Diffuse"
}
Код тестового скрипта создающего куб:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Create_Test_Cube : MonoBehaviour
{
[Header("Компоненты:")]
public MeshRenderer mesh_ren;
public MeshFilter mesh_fil;
public Mesh mesh;
[Space]
public Material mat;
[Header("Параметры")]
public List<Vector3> vertices = new List<Vector3>();
public List<int> triangles = new List<int>();
public List<Vector2> uvs = new List<Vector2>();
void Start()
{
GetMesh();
CreateCube();
}
[ContextMenu("Create Cube")]
public void CreateCube()
{
if(vertices.Count > 0)
ClearMesh();
CreateAllSide();
SetMesh();
}
void GetMesh()
{
mesh_ren = this.gameObject.GetComponent<MeshRenderer>();
if (mesh_ren == null)
mesh_ren = this.gameObject.AddComponent<MeshRenderer>();
mesh_ren.material = mat;
mesh_fil = this.gameObject.GetComponent<MeshFilter>();
if (mesh_fil == null)
mesh_fil = this.gameObject.AddComponent<MeshFilter>();
}
void SetMesh()
{
mesh = new Mesh();
mesh.name = "test_cube_mesh";
mesh.SetVertices(vertices);
mesh.SetTriangles(triangles, 0);
mesh.SetUVs(0, uvs);
mesh.RecalculateBounds();
mesh.RecalculateNormals();
mesh_fil.mesh = mesh;
}
void ClearMesh()
{
vertices.Clear();
triangles.Clear();
uvs.Clear();
}
/// *** *** ///
void CreateAllSide()
{
vertices.Add(new Vector3(-0.5f, -0.5f, -0.5f));
vertices.Add(new Vector3(-0.5f, 0.5f, -0.5f));
vertices.Add(new Vector3( 0.5f, -0.5f, -0.5f));
vertices.Add(new Vector3( 0.5f, 0.5f, -0.5f));
AddTrianglesSquare();
vertices.Add(new Vector3( 0.5f, 0.5f, 0.5f));
vertices.Add(new Vector3( 0.5f, 0.5f, -0.5f));
vertices.Add(new Vector3(-0.5f, 0.5f, 0.5f));
vertices.Add(new Vector3(-0.5f, 0.5f, -0.5f));
AddTrianglesSquare();
vertices.Add(new Vector3( 0.5f, -0.5f, -0.5f));
vertices.Add(new Vector3( 0.5f, 0.5f, -0.5f));
vertices.Add(new Vector3( 0.5f, -0.5f, 0.5f));
vertices.Add(new Vector3( 0.5f, 0.5f, 0.5f));
AddTrianglesSquare();
vertices.Add(new Vector3( 0.5f, -0.5f, 0.5f));
vertices.Add(new Vector3( 0.5f, 0.5f, 0.5f));
vertices.Add(new Vector3(-0.5f, -0.5f, 0.5f));
vertices.Add(new Vector3(-0.5f, 0.5f, 0.5f));
AddTrianglesSquare();
vertices.Add(new Vector3(-0.5f, -0.5f, 0.5f));
vertices.Add(new Vector3(-0.5f, 0.5f, 0.5f));
vertices.Add(new Vector3(-0.5f, -0.5f, -0.5f));
vertices.Add(new Vector3(-0.5f, 0.5f, -0.5f));
AddTrianglesSquare();
vertices.Add(new Vector3( 0.5f, -0.5f, -0.5f));
vertices.Add(new Vector3( 0.5f, -0.5f, 0.5f));
vertices.Add(new Vector3(-0.5f, -0.5f, -0.5f));
vertices.Add(new Vector3(-0.5f, -0.5f, 0.5f));
AddTrianglesSquare();
}
void AddTrianglesSquare()
{
triangles.Add(vertices.Count - 4); // 0
triangles.Add(vertices.Count - 3); // 1
triangles.Add(vertices.Count - 2); // 2
triangles.Add(vertices.Count - 2); // 2
triangles.Add(vertices.Count - 3); // 1
triangles.Add(vertices.Count - 1); // 3
}
}
Я все еще новичок в шейдерах и может просто не вижу чего-то очевидного, но у меня не получилось найти проблему.