ПрограммированиеФорумГрафика

[Uniy3D] Шейдер воды на основе 3D текстуры с шумом Перлина

#0
19:48, 30 окт 2014

Приветствую всех. Разбираюсь с шейдерами, встала задача реализовать быстро водный шейдер в юнити используся CGPROGRAMM.
После долгих ресерчей, нашел нужный вариант в программе RenderMonkey (Ocean Effect).
Работает он на основе volume текстуры в формате dds из который по времени производится выборка слоя и тем самым достигается эффект изменения волны (+ движение текстурных координат по времени)

Попробовал его перенести в Unity Shader Lab и столкнулся с тем что dds не поддерживается, вообще не понятно как volume текстуру экспортировать.
Решил не использовать volume текстуру, вместо нее использовать один слой и просто смещать в нем текстурные координаты.
В рендермонкей все работает (волна только не шевелится, ну да бог с ней). В Unity же даже рядом такого эффекта нет.

Вот что в рендер монкей.
Эффект воды РендерМонкей | [Uniy3D] Шейдер воды на основе 3D текстуры с шумом Перлина

В Unity просто искаженная текстура Cubemap'a ((которая кстати не движется даже наверное unity_DeltaTime какой-то не такой). И намека нет на волну, при этом и отражение кривое (ну тут в целом скорее из-за того, что плоскость там обычная).
Вопрос, где ошибка? Сам грешу на расчет вектора отражения исходя из текстуры шума, но в чем именно ошибка и почему в рендермонкей все нормально не могу понять. Буду рад помощи или задание направления в котором двигаться)).

Код шейдера Unity

Shader "Custom/SimpleWater" 
{
  Properties 
  {
    _NoiseWave ("Wave Perlin Noise Texture", 2D) = "white" {}    
    _Cubemap ("Reflection Texrture", CUBE) = "" {}
    _WaterColor ("Water Color", Color) = (1, 1, 1, 0)    
    _WaterColorAmount ("Water Color Amount", float) = 0.5
    _WaterSpeedX ("Water Speed X", float) = 0.5
    _WaterSpeedY ("Water Speed Y", float) = 0.5
    _WaveSpeed ("Wave Speed", float) = 0.5
    _WaveScale ("Wave Scale", Vector) = (1, 1, 1, 1)    
  }

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

    Pass 
    {      
      CGPROGRAM
      #include "UnityCG.cginc"
      #pragma vertex VS_MAIN
      #pragma fragment PS_MAIN
                   
      sampler2D _NoiseWave;
      samplerCUBE _Cubemap;

      uniform float4 _WaterColor;
      uniform float _WaterSpeedX;
      uniform float _WaterSpeedY;      
      uniform float _WaterColorAmount;
      uniform float4 _WaveScale;
      uniform float _WaveSpeed;      

      struct VS_INPUT
      {
        float4 Pos          : POSITION;        
        float3 Normal       : TEXCOORD0;        
      };

      struct VS_OUTPUT
      {
        float4 Pos          : POSITION;
        float3 scaledPos    : TEXCOORD0;
        float3 Normal       : TEXCOORD1;
        float3 ViewVector   : TEXCOORD2;      
      };

      struct PS_INPUT
      {        
         float3 Pos: TEXCOORD0;
         float3 Normal: TEXCOORD1;
         float3 ViewVector: TEXCOORD2;
      };  
    
      VS_OUTPUT VS_MAIN(VS_INPUT input)    
      {
        VS_OUTPUT  output;        
        output.Pos = mul(UNITY_MATRIX_MVP, input.Pos); 
        output.scaledPos = output.Pos.xyz * _WaveScale; просто сжатие или растяжение волны
        output.ViewVector.xzy = output.Pos - _WorldSpaceCameraPos; // вектор направления взгляда из камеры
        output.Normal = input.Normal;

        return output;
      }    

      float4 PS_MAIN(PS_INPUT input) : COLOR
      {
         input.Pos.x += _WaterSpeedX  * unity_DeltaTime.y; // движение текстурных координат         
         input.Pos.y += _WaterSpeedY  * unity_DeltaTime.y;         

         float4 noiseValue = tex2D(_NoiseWave, input.Pos); // получаем пиксель из шумовой текстуры
         
         float3 noiseBumping = 2 * noiseValue - 1;
         noiseBumping.xy *= 0.15;         
         noiseBumping.z = 0.8 * abs(noiseBumping.z) + 0.2;          
         noiseBumping = normalize(input.Normal + noiseBumping);
         
         float3 reflectionVector = reflect(input.ViewVector, noiseBumping); // получаем вектор отражения исходя из направления взгляда, и расчитанного по сути положения поверхности волны (нормали поверхности всегда положительные).
         float4 reflectionColor = texCUBE(_Cubemap, reflectionVector.xyz); // получаем цвет отраженной текстуры из кубмапа.
      
         return lerp(_WaterColor, reflectionColor, _WaterColorAmount); // смешиваем цвет воды и цвет отраженной текстуры.
      }

      ENDCG
    }
  } 

  FallBack "Diffuse"
}
#1
21:13, 30 окт 2014

DeltaTime это же время, прошедшее за кадр, а не глобальное. Тебе нужен _Time
http://docs.unity3d.com/Manual/SL-BuiltinValues.html

#2
21:21, 30 окт 2014

Mr F
Ну это допустим, тока это не решает остальных проблем. Потому что даже поставив _Time, я не вижу как все смещается, из-за отсутствия самой волны))

#3
23:34, 30 окт 2014

Fate Li
> столкнулся с тем что dds не поддерживается, вообще не понятно как volume текстуру экспортировать.
Загружать послойно набором 2D текстур, например .png.

#4
11:58, 31 окт 2014

Да юнитехи это почему-то упустили

Вот даже сами приводят код
http://docs.unity3d.com/Manual/class-Texture3D.html

В принципе не сложно через редактор самому состряпать 3D текстуры из 2D текстур и потом хранить через scriptableobject

Если получится, просьба отпишись здесь, особенно на мобилках

#5
14:42, 31 окт 2014

DanielDem
> http://docs.unity3d.com/Manual/class-Texture3D.html
>
> В принципе не сложно через редактор самому состряпать 3D текстуры из 2D текстур
> и потом хранить через scriptableobject
Не забываем еще, что для 3д текстуры ПРОшка нужна)) (ну и gles3 на мобилках)
Fate Li
wrapMode у текстуры шума какой? попробуй накатать сначала простой шойдер с нормалмапой/рефлекшоном, а потом усложняй его))

#6
14:44, 31 окт 2014

На Asset Store есть библиотека, которая считает разные типы шумов прямо внутри шейдеров:

http://forum.unity3d.com/threads/turbulence-library-massive-colle… e-gpu.143649/
https://www.assetstore.unity3d.com/#!/content/3957

Вроде любопытная штука, но всё руки не доходят посмотреть.

#7
15:39, 31 окт 2014

Belfegnar
> wrapMode у текстуры шума какой? попробуй накатать сначала простой шойдер с
> нормалмапой/рефлекшоном, а потом усложняй его))
Сейчас пошел по этому пути. Осталось сделать эффект самой волны

alexzzzz
очень любопытная), надо потестить

#8
16:07, 31 окт 2014

alexzzzz
Затестил этот шумогенератор - все круто, но есть проблема, генератор возвращает float (0.0 - 1.0) значение цвета в чб формате по сути.
Как из этого получить вектор нормали для того, чтобы рефлекшен правильно отработал, пока что не придумал)

#9
16:14, 31 окт 2014

Belfegnar
> придется делать выборку минимум по четырем соседним точкам и считать нормаль.
Достаточно трёх соседей, как-то так:

N(x,y,z) = normalize(vec3(v(x + d,y,z) - v(x,y,z),v(x,y + d,z) - v(x,y,z),v(x,y,z + d) - v(x,y,z)))
#10
23:24, 31 окт 2014

После долгих мучений, таки родилось что-то похожее на воду (есть еще баги и рефракцию надо добавить), но прогресс идет.

К сожалению найти нормаль из карты высот по 3 точкам не вышло, умучался весь. Наилучший результат дала эта методика
4 - это наш текущий тексель (значение высоты из шума Перлина).

[6][7][8]
[3][4][5]
[0][1][2]

vec3 n;
n.x = scale * -(s[2]-s[0]+2*(s[5]-s[3])+s[8]-s[6]);
n.y = scale * -(s[6]-s[0]+2*(s[7]-s[1])+s[8]-s[2]);
n.z = 1.0;
n = normalize(n); эта наша искомая нормаль, только для юнити y и z местами поменять надо.

Ниже ролик, что сейчас выходит. Работа продолжается)
http://youtu.be/gghanXGwde0

PS: шейдер далек от оптимизации весьма, я боюсь мобилкам такое не прожевать)) но задача не стояла про них).
PS2: код шейдера скину позже

#11
23:35, 31 окт 2014

Fate Li
> и рефракцию надо добавить

И рефлекцию :)

#12
0:05, 1 ноя 2014

Fate Li
> К сожалению найти нормаль из карты высот по 3 точкам не вышло, умучался весь.
Там будет результат некорректный на гребнях.

> Наилучший результат дала эта методика
> 4 - это наш текущий тексель (значение высоты из шума Перлина).
Возьми векторное произведение векторов крест-накрест ( если развернуть то можно будет упростить тк оффсеты по плоскости XZ будут фиксированными )
Примерно так: N = normalize( cross( left, top ) + cross( right, bottom ) )
Высоту можно двумя fetch4 достать если SM4.

#13
21:23, 1 ноя 2014

0BF75A9A4B3A2FC60FEA
> Высоту можно двумя fetch4 достать если SM4.
шумогенератор уже требует 3.0, до 4.0 не дорос еще))

alexzzzz
> И рефлекцию :)
рефлекцию добавил - нашел ошибку в шейдере, не добавлял к конечному цвету цвет отраженной текстуры) поэтому вода очень странно смотрелась.

Разбираюсь с рефракцией.

Насколько я понимаю она делается минимум в 2 прохода, сначала захват текстуры относительно объекта с шейдером, затем искажение этой текстуры (через хитропопые сдвиги UV координат).
Все верно?

т.е. в рамках ShaderLab получиться

SubShader
{
     GrabPass {}
      
     Pass
     {
          sampler2D _GrabTexture;

          // дале творим че нам надо с этой текстурой
     }
}

PS: я уже столько насмотрелся на шумы, что мне кажется стал выделять циклы повторения)

ПрограммированиеФорумГрафика

Тема в архиве.