Приветствую всех. Разбираюсь с шейдерами, встала задача реализовать быстро водный шейдер в юнити используся CGPROGRAMM.
После долгих ресерчей, нашел нужный вариант в программе RenderMonkey (Ocean Effect).
Работает он на основе volume текстуры в формате dds из который по времени производится выборка слоя и тем самым достигается эффект изменения волны (+ движение текстурных координат по времени)
Попробовал его перенести в Unity Shader Lab и столкнулся с тем что dds не поддерживается, вообще не понятно как volume текстуру экспортировать.
Решил не использовать volume текстуру, вместо нее использовать один слой и просто смещать в нем текстурные координаты.
В рендермонкей все работает (волна только не шевелится, ну да бог с ней). В Unity же даже рядом такого эффекта нет.
Вот что в рендер монкей.
![Эффект воды РендерМонкей Эффект воды РендерМонкей | [Uniy3D] Шейдер воды на основе 3D текстуры с шумом Перлина](https://gamedev.ru/files/images/rendermonkeyshader.jpg)
В 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"
}DeltaTime это же время, прошедшее за кадр, а не глобальное. Тебе нужен _Time
http://docs.unity3d.com/Manual/SL-BuiltinValues.html
Mr F
Ну это допустим, тока это не решает остальных проблем. Потому что даже поставив _Time, я не вижу как все смещается, из-за отсутствия самой волны))
Fate Li
> столкнулся с тем что dds не поддерживается, вообще не понятно как volume текстуру экспортировать.
Загружать послойно набором 2D текстур, например .png.
Да юнитехи это почему-то упустили
Вот даже сами приводят код
http://docs.unity3d.com/Manual/class-Texture3D.html
В принципе не сложно через редактор самому состряпать 3D текстуры из 2D текстур и потом хранить через scriptableobject
Если получится, просьба отпишись здесь, особенно на мобилках
DanielDem
> http://docs.unity3d.com/Manual/class-Texture3D.html
>
> В принципе не сложно через редактор самому состряпать 3D текстуры из 2D текстур
> и потом хранить через scriptableobject
Не забываем еще, что для 3д текстуры ПРОшка нужна)) (ну и gles3 на мобилках)
Fate Li
wrapMode у текстуры шума какой? попробуй накатать сначала простой шойдер с нормалмапой/рефлекшоном, а потом усложняй его))
На Asset Store есть библиотека, которая считает разные типы шумов прямо внутри шейдеров:
http://forum.unity3d.com/threads/turbulence-library-massive-colle… e-gpu.143649/
https://www.assetstore.unity3d.com/#!/content/3957
Вроде любопытная штука, но всё руки не доходят посмотреть.
Belfegnar
> wrapMode у текстуры шума какой? попробуй накатать сначала простой шойдер с
> нормалмапой/рефлекшоном, а потом усложняй его))
Сейчас пошел по этому пути. Осталось сделать эффект самой волны
alexzzzz
очень любопытная), надо потестить
alexzzzz
Затестил этот шумогенератор - все круто, но есть проблема, генератор возвращает float (0.0 - 1.0) значение цвета в чб формате по сути.
Как из этого получить вектор нормали для того, чтобы рефлекшен правильно отработал, пока что не придумал)
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)))
После долгих мучений, таки родилось что-то похожее на воду (есть еще баги и рефракцию надо добавить), но прогресс идет.
К сожалению найти нормаль из карты высот по 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: код шейдера скину позже
Fate Li
> и рефракцию надо добавить
И рефлекцию :)
Fate Li
> К сожалению найти нормаль из карты высот по 3 точкам не вышло, умучался весь.
Там будет результат некорректный на гребнях.
> Наилучший результат дала эта методика
> 4 - это наш текущий тексель (значение высоты из шума Перлина).
Возьми векторное произведение векторов крест-накрест ( если развернуть то можно будет упростить тк оффсеты по плоскости XZ будут фиксированными )
Примерно так: N = normalize( cross( left, top ) + cross( right, bottom ) )
Высоту можно двумя fetch4 достать если SM4.
0BF75A9A4B3A2FC60FEA
> Высоту можно двумя fetch4 достать если SM4.
шумогенератор уже требует 3.0, до 4.0 не дорос еще))
alexzzzz
> И рефлекцию :)
рефлекцию добавил - нашел ошибку в шейдере, не добавлял к конечному цвету цвет отраженной текстуры) поэтому вода очень странно смотрелась.
Разбираюсь с рефракцией.
Насколько я понимаю она делается минимум в 2 прохода, сначала захват текстуры относительно объекта с шейдером, затем искажение этой текстуры (через хитропопые сдвиги UV координат).
Все верно?
т.е. в рамках ShaderLab получиться
SubShader
{
GrabPass {}
Pass
{
sampler2D _GrabTexture;
// дале творим че нам надо с этой текстурой
}
}PS: я уже столько насмотрелся на шумы, что мне кажется стал выделять циклы повторения)
Тема в архиве.