2017-01-21 9 views
0

просто интересно, если кто-нибудь может протянуть руку, провел часы до сих пор по этому поводу:Unity Shader нормалей неправильно

enter image description here

Кто-нибудь знает, почему это происходит ??? Когда я двигаюсь, дно всегда ярче верхнего.

основной поток верт: * получить точку, что + X и точка, + Z для использования в поперечном умножении позже, чтобы получить нормальный

* перевести эти точки в мировом пространстве, так что я могу ищу obsticals в реальном пространстве

* фигура из оси у смещения для нашей точки + дополнительных услуг, сделанных на первом этапе для кросса

* значение объекта приращения пространства пути перемещения

* найти новое нормальный используя перекрестную

* установить нормальный

* множество вершин.

То, что я думаю, что происходит в том, что нормали на неверной оси ... любая помощь apreciated

enter image description here

Shader "Custom/ExtrudePointArray" { 
    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 

      //water stuff 
      _Scale("Scale", float) = 1 
      _Speed("Speed", float) = 1 
      _Frequency("Frequency", float) = 1 

      // wave maker 
      _Height("_Height", float) = 1.0 
      _Extrude_Close("_Extrude_Close", float) = 3.0 

      [HideInInspector]_ExtrudePointCount("_ExtrudePointCount", int) = 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 vertex:vert 

     //debug normals 
//#pragma surface surf Standard fullforwardshadows vertex:vert finalcolor:showNormals 

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

     sampler2D _MainTex; 

     struct Input { 
      float2 uv_MainTex; 
      float3 newNormal; 
      half3 debugColor; 
     }; 

     half _Glossiness; 
     half _Metallic; 
     fixed4 _Color; 

     // water stuff 
     float _Scale, _Speed, _Frequency; 


     float _Extrude_Close, _Height; 
     uniform float _ExtrudePointX[20], _ExtrudePointZ[20], _ExtrudePointCount; 

     float calcWaveHeight(float2 worvldLoc) { 
      //disruption 
      half offsetvertDis = ((worvldLoc.x * worvldLoc.x) + (worvldLoc.y * worvldLoc.y)); 
      half valueDis = _Scale * sin(_Time.w * _Speed + offsetvertDis * _Frequency*1000); 
      // diag wave 
      half offsetvertDiag = worvldLoc.x +worvldLoc.y; 
      half valueDiag = _Scale * sin(_Time.w * _Speed * _Frequency + offsetvertDiag); 

      return valueDis + valueDiag; 
     } 

     float calcDistFromPoint(float2 worldLoc, float2 pointToCheck) { 
      float closePercent = 0; 
      float dist = abs(distance(worldLoc, pointToCheck)); 

      if (dist < _Extrude_Close) { 
       closePercent = (_Extrude_Close - dist/_Extrude_Close); 
      } 
      return closePercent; 
     } 

     float calcVertNewPos(float3 worldLoc) { 
      float highestPos = 0; 
      float2 worldLocHor = float2(worldLoc.x, worldLoc.z); 

      for (int i = 0; i < _ExtrudePointCount; i++) { 
       float2 boxPos = float2(_ExtrudePointX[i], _ExtrudePointZ[i]); 

       float newPos = calcDistFromPoint(worldLocHor, boxPos) * _Height; 

       // the value returned is a float so don't check for zero, check that it's not above zero 
       if (newPos < 0.001) { 
        newPos = calcWaveHeight(worldLocHor); 
       } 

       if (newPos > highestPos) { 
        highestPos = newPos; 
       } 
       //highestPos = worldLoc.x * i; 
      } 

      return highestPos; 
     } 

     void vert(inout appdata_full v, out Input o) 
     { 
      UNITY_INITIALIZE_OUTPUT(Input, o); 

      float3 v0 = v.vertex.xyz; 
      float3 v1 = v0 + float3(0.05, 0, 0); // +X 
      float3 v2 = v0 + float3(0, 0, 0.05); // +Z 

      float3 w0 = mul(unity_ObjectToWorld, v.vertex).xyz; 
      float3 w1 = w0 + float3(0.05, 0, 0); // +X 
      float3 w2 = w0 + float3(0, 0, 0.05); // +Z 

      float h0 = calcVertNewPos(w0); 
      float h1 = calcVertNewPos(w1); 
      float h2 = calcVertNewPos(w2); 

      v0.y = h0; 
      v1.y = h1; 
      v2.y = h2; 

      float3 vna = cross(v2 - v0, v1 - v0); 

      // debug 
      //o.debugColor = (normalize(v0) * 0.5) + 0.5; 
      //o.debugColor = (normalize(v.vertex.xyz) * 0.5) + 0.5; 
      //o.debugColor = (normalize(vna) * 0.5) + 0.5; 

      // Put normals back in object space 
      //float3x3 worlspace = float3x3(unity_WorldToObject.xyz); 

      v.normal = normalize(vna); 
      o.newNormal = v.normal; 

      v.vertex.xyz = v0; 
     } 

     void showNormals(Input IN, SurfaceOutputStandard o, inout fixed4 color) { 
      color.rgb = IN.debugColor.rgb; 
      color.a = 1; 
     } 

     void surf (Input IN, inout SurfaceOutputStandard o) { 
      // Albedo comes from a texture tinted by color 
      fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; 
      o.Albedo = c.rgb; 
      // Metallic and smoothness come from slider variables 
      o.Metallic = _Metallic; 
      o.Smoothness = _Glossiness; 
      o.Alpha = c.a; 
      o.Normal = IN.newNormal; 
     } 
     ENDCG 
    } 
    FallBack "Diffuse" 
} 

Я знаю, что я почти наверняка делает Somthing неправильно reguarding нормального расчета. Но я не могу найти на нем никаких хороших ресурсов, и единственное, что я нашел: https://www.youtube.com/watch?v=1G37-Yav2ZM был написан для более ранней версии шейдерного языка, и сайт для загрузки источника теперь вредоносный :(Это убивает меня

Также здесь код, который работает выдавливание точек затенения, если вам это нужно:

using System; 
using System.Collections; 
using System.Collections.Generic; 
using UnityEngine; 

public class ExtrudePoint : MonoBehaviour { 

    public GameObject[] Waves; 
    private Renderer _renderer; 

    // Use this for initialization 
    void Start() 
    { 
     if (Waves == null || Waves.Length > 8) 
     { 
      throw new Exception("Max of 8 waves, min of 1"); 
     } 

     _renderer = GetComponent<Renderer>(); 

    } 

    // Update is called once per frame 
    void Update() 
    { 
     int totWaves = 0; 
     // probably slow 
     var floatArrayX = new float[Waves.Length]; 
     var floatArrayZ = new float[Waves.Length]; 
     for (var i = 0; i < Waves.Length; i++) 
     { 
      var wave = Waves[i]; 
      if (wave != null) 
      { 
       totWaves++; 
       floatArrayX[totWaves - 1] = wave.gameObject.transform.position.x; 
       floatArrayZ[totWaves - 1] = wave.gameObject.transform.position.z; 
       //_renderer.material.SetFloat(WavePointExtruderConstants._ExtrudePointX, wave.gameObject.transform.position.x); 
       //_renderer.material.SetFloat(WavePointExtruderConstants._ExtrudePointZ, wave.gameObject.transform.position.z); 

       //Debug.Log(wave.gameObject.transform.position); 
      } 
     } 

     // probably slow 
     var xWaveArray = new float[totWaves]; 
     var zWaveArray = new float[totWaves]; 

     for(int i = 0; i < totWaves; i++) 
     { 
      xWaveArray[i] = floatArrayX[i]; 
      zWaveArray[i] = floatArrayZ[i]; 
     } 

     //Debug.Log(totWaves); 

     _renderer.material.SetFloatArray(WavePointExtruderConstants._ExtrudePointX, xWaveArray); 
     _renderer.material.SetFloatArray(WavePointExtruderConstants._ExtrudePointZ, zWaveArray); 
     _renderer.material.SetFloat(WavePointExtruderConstants._ExtrudePointCount, totWaves); 
    } 
} 

public static class WavePointExtruderConstants 
{ 
    public static string _Extrude_Close = "_Extrude_Close"; 
    public static string _ExtrudePointX = "_ExtrudePointX"; 
    public static string _ExtrudePointZ = "_ExtrudePointZ"; 
    public static string _ExtrudePointCount = "_ExtrudePointCount"; 
} 

установка представляет собой равнину с шейдерным материалом + кодом выше. Затем вы производите любой игровой объект и помещаете его в массив Waves.

ответ

1

В вашем шейдере есть как минимум две проблемы.

Во-первых, когда вы делаете o.Normal = myNormal; в поверхностном шейдере, то myNormal как ожидается, будет в касательном пространстве, но ваш шейдер проходит нормальный вычисленная в мировом пространстве.

Так вот исправление для этого (единство конкретного кода для преобразования из мирового пространства в касательном пространстве):

//---- before 
//v.normal = normalize(vna); 
//o.newNormal = v.normal; 

//--- after 
float3 worldNormal = normalize(vna); 
// unity macro which setups "world space->tangent space" matrix in "rotation" variable 
TANGENT_SPACE_ROTATION; 
float3 tangentSpaceNormal = mul(rotation, worldNormal); 
o.newNormal = tangentSpaceNormal; 

Во-вторых, код, который вы используете вычисления нормали к вершине на самом деле код для вычисления нормали многоугольника , определяемого (v0, v1, v2), это не совсем неправильно, но, вероятно, не будет работать так, как вы ожидаете.

Хороший способ вычислить нормаль вершины V является:

  1. получить все грани V принадлежит
  2. вычислить нормали всех этих граней
  3. V нормально тогда среднее из лицевые нормали

Здесь вы работаете с плоской сеткой, поэтому каждая грань представляет собой квадрант, и каждая вершина принадлежит четырем квадратам (это не верно для граничных вершин, но в вашем случае это не проблема).

Так вы текущий код, который добавляет +0,05 в X & Z на самом деле вычисления нормаль четырехъядерным/лица, который находится на верхней правой части вершины (если причина с видом сверху Unity).

Для того, чтобы получить нормальное значение, вам нужно будет вычислить нормали трех других квадратов/квадратов, это даст вам 4 нормали и, наконец, усреднив их, вы получите нормальную вершину (суммируйте их и разделите на 4).

+0

даст вам возможность, когда я смогу! благодаря –