├── .gitattributes ├── LICENSE ├── README.md ├── screenshots ├── 1.jpg ├── 2.jpg ├── 3.jpg ├── preview.jpg └── water_settings.jpg ├── scripts └── WaterShaderGUI.cs ├── shaders ├── convert_shaders.bat ├── glsl │ ├── water.frag.spv │ └── water.vert.spv ├── hlsl │ ├── bicubic.cginc │ ├── normals.cginc │ ├── snoise.cginc │ ├── unpack.cginc │ ├── water.fx │ └── water │ │ ├── depth.cginc │ │ ├── displacement.cginc │ │ ├── foam.cginc │ │ ├── meansky.cginc │ │ ├── radiance.cginc │ │ └── waves.cginc └── unity │ ├── conversion.cginc │ └── water.shader └── textures ├── foam.png ├── foam_shore.png ├── skybox.png ├── water_height.png └── water_normal.png /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 tuxalin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Procedural Water 2 | A procedural water shader, multi platforrm support(Unity, GLSL, HLSL), for a variety of water types. 3 | 4 | ![water-main](https://orig00.deviantart.net/9b8c/f/2017/347/5/0/tropical_water_by_tuxalin-dbwmtea.jpg) 5 | 6 | ## Features 7 | The water shader is designed for various water types: lakes, ocean and rivers. 8 | 9 | Current implemented features are: 10 | - multiple maps(height, normal, shore, sky) 11 | - configurable refraction and reflection 12 | - colour extiction based water depth 13 | - mean sky radiance 14 | - displacement 15 | - enabling/disabling features 16 | - bicubic filtering 17 | - configurable shore foam 18 | - HLSL/GLSL/Unity support(for GLSL use the conversion script to SPIR-V) 19 | 20 | ## Usage 21 | 22 | The shader requires refraction and reflection maps of the scene, the refraction map also requires the depth. 23 | See the textures folder for examples of sky, shore, foam, height and normal maps. 24 | 25 | Certain features can be switched off via the following defines: 26 | - USE_DISPLACEMENT, adds displacement of gerstner and sinve waves, with an additional height map 27 | - USE_MEAN_SKY_RADIANCE, adds mean sky radiance reflectance, requires a sky map 28 | - USE_FILTERING, enable/disable bicubic filtering 29 | - BLINN_PHONG, use simplified specular 30 | 31 | ## Preview 32 | 33 | [![video](screenshots/preview.jpg)](https://www.youtube.com/watch?v=MCG2yPbchj0) 34 | 35 | ![water-prev1](https://orig00.deviantart.net/159e/f/2017/347/a/7/tropical_water_by_tuxalin-dbwmvej.jpg) 36 | 37 | Composition of the water(final, shore, refraction+depth colour, reflection + distortion, diffuse + ambient, normals): 38 | ![water-layers](https://orig00.deviantart.net/1199/f/2017/347/1/5/water_layers_by_tuxalin-dbwmyke.jpg) 39 | 40 | ![water-settings](screenshots/water_settings.jpg) 41 | 42 | ## Future improvements 43 | 44 | Planned improvements are: 45 | - caustics 46 | - underwater mode with light rays 47 | - cascading depth maps 48 | - improved water displacement/simulation(FFT) 49 | 50 | ## Contributing 51 | 52 | Based on: 53 | - [Rendering Water as a Post-process Effect by Wojciech Toman](https://www.gamedev.net/articles/programming/graphics/rendering-water-as-a-post-process-effect-r2642/). 54 | - [GPU Gems Chapter 1. Effective Water Simulation from Physical Models, by Mark Finch and Cyan Worlds](http://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch01.html) 55 | - [Real-time Realistic Ocean Lighting using Seamless Transitions from Geometry to BRDF by Eric Bruneton, Fabrice Neyret, Nicolas Holzschuch](https://hal.inria.fr/inria-00443630/file/article-1.pdf) 56 | - [HLSL 2D simplex noise function by Ian McEwan, Ashima Arts](https://github.com/ashima/webgl-noise) 57 | 58 | Bug reports and pull requests are welcome on GitHub at https://github.com/tuxalin/water-shader. 59 | 60 | ## License 61 | 62 | The code is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 63 | -------------------------------------------------------------------------------- /screenshots/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/screenshots/1.jpg -------------------------------------------------------------------------------- /screenshots/2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/screenshots/2.jpg -------------------------------------------------------------------------------- /screenshots/3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/screenshots/3.jpg -------------------------------------------------------------------------------- /screenshots/preview.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/screenshots/preview.jpg -------------------------------------------------------------------------------- /screenshots/water_settings.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/screenshots/water_settings.jpg -------------------------------------------------------------------------------- /scripts/WaterShaderGUI.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | public class WaterShaderGUI : ShaderGUI 7 | { 8 | private string[] displacementProps = new string[] { "_HeightTexture", "_HeightIntensity", "_WaveTiling", "_WaveAmplitudeFactor", "_WaveSteepness", "_WaveAmplitude" }; 9 | private string[] meanSkyProps = new string[] { "_RadianceFactor" }; 10 | private string[] foamProps = new string[] { "_ShoreTexture", "_FoamTexture", "_FoamTiling", "_FoamNoise", "_FoamSpeed", "_FoamIntensity" }; 11 | 12 | private void CheckFeature(Material targetMat, MaterialProperty[] materialProperties, string toggleName, string featureName, string[] properties, HashSet disabledProperties) 13 | { 14 | bool isEnabled = Array.IndexOf(targetMat.shaderKeywords, featureName) != -1; 15 | 16 | MaterialProperty toggle = ShaderGUI.FindProperty(toggleName, materialProperties); 17 | if (toggle.floatValue == 0 && isEnabled == false) 18 | { 19 | foreach (string name in properties) 20 | { 21 | disabledProperties.Add(name); 22 | } 23 | } 24 | } 25 | 26 | public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] properties) 27 | { 28 | Material targetMat = materialEditor.target as Material; 29 | 30 | HashSet disabledProperties = new HashSet(); 31 | CheckFeature(targetMat, properties, "_UseDisplacement", "USE_DISPLACEMENT", displacementProps, disabledProperties); 32 | CheckFeature(targetMat, properties, "_UseMeanSky", "USE_MEAN_SKY_RADIANCE", meanSkyProps, disabledProperties); 33 | CheckFeature(targetMat, properties, "_UseFoam", "USE_FOAM", foamProps, disabledProperties); 34 | 35 | // show only visible properties based on enabled features 36 | foreach (MaterialProperty property in properties) 37 | { 38 | if (property.name != "_ReflectionTexture" && !disabledProperties.Contains(property.name)) 39 | materialEditor.ShaderProperty(property, property.displayName); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /shaders/convert_shaders.bat: -------------------------------------------------------------------------------- 1 | 2 | @ECHO OFF >NUL 3 | 4 | if "%~1" == "" (set WORK_PATH=%cd%) else (set WORK_PATH=%~1) 5 | 6 | echo. 7 | echo "Converting shaders in: "%WORK_PATH% 8 | echo. 9 | 10 | %VULKAN_SDK%/Bin32/glslangValidator.exe -S vert -e vert -o glsl/water.vert.spv -V -D hlsl/water.fx 11 | %VULKAN_SDK%/Bin32/glslangValidator.exe -S frag -e frag -o glsl/water.frag.spv -V -D hlsl/water.fx 12 | -------------------------------------------------------------------------------- /shaders/glsl/water.frag.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/shaders/glsl/water.frag.spv -------------------------------------------------------------------------------- /shaders/glsl/water.vert.spv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/shaders/glsl/water.vert.spv -------------------------------------------------------------------------------- /shaders/hlsl/bicubic.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Bicubic filtering functions 3 | // 4 | 5 | float4 cubic(float v) 6 | { 7 | float4 n = float4(1.0, 2.0, 3.0, 4.0) - v; 8 | float4 s = n * n * n; 9 | float x = s.x; 10 | float y = s.y - 4.0 * s.x; 11 | float z = s.z - 4.0 * s.y + 6.0 * s.x; 12 | float w = 6.0 - x - y - z; 13 | return float4(x, y, z, w) * (1.0 / 6.0); 14 | } 15 | 16 | // 4 taps bicubic filtering, requires sampler to use bilinear filtering 17 | float4 tex2DBicubic(sampler2D tex, float texSize, float2 texCoords) 18 | { 19 | float2 texSize2 = texSize; 20 | float2 invTexSize = 1.0 / texSize2; 21 | 22 | texCoords = texCoords * texSize2 - 0.5; 23 | float2 fxy = frac(texCoords); 24 | texCoords -= fxy; 25 | 26 | float4 xcubic = cubic(fxy.x); 27 | float4 ycubic = cubic(fxy.y); 28 | 29 | float4 c = texCoords.xxyy + float2(-0.5, +1.5).xyxy; 30 | 31 | float4 s = float4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); 32 | float4 offset = c + float4(xcubic.yw, ycubic.yw) / s; 33 | 34 | offset *= invTexSize.xxyy; 35 | 36 | float4 sample0 = tex2D(tex, offset.xz); 37 | float4 sample1 = tex2D(tex, offset.yz); 38 | float4 sample2 = tex2D(tex, offset.xw); 39 | float4 sample3 = tex2D(tex, offset.yw); 40 | 41 | float sx = s.x / (s.x + s.y); 42 | float sy = s.z / (s.z + s.w); 43 | 44 | return lerp(lerp(sample3, sample2, sx), lerp(sample1, sample0, sx), sy); 45 | } 46 | -------------------------------------------------------------------------------- /shaders/hlsl/normals.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Utilties for normal mapping 3 | // 4 | 5 | // Project the surface gradient (dhdx, dhdy) onto the surface (n, dpdx, dpdy). 6 | float3 ComputeSurfaceGradient(float3 n, float3 dpdx, float3 dpdy, float dhdx, float dhdy) 7 | { 8 | float3 r1 = cross(dpdy, n); 9 | float3 r2 = cross(n, dpdx); 10 | 11 | return (r1 * dhdx + r2 * dhdy) / dot(dpdx, r1); 12 | } 13 | 14 | // Move the normal away from the surface normal in the opposite surface gradient direction. 15 | float3 PerturbNormal(float3 n, float3 dpdx, float3 dpdy, float dhdx, float dhdy) 16 | { 17 | return normalize(n - ComputeSurfaceGradient(n, dpdx, dpdy, dhdx, dhdy)); 18 | } 19 | 20 | // Returns the surface normal using screen-space partial derivatives of the height field. 21 | float3 ComputeSurfaceNormal(float3 position, float3 normal, float height) 22 | { 23 | float3 dpdx = ddx(position); 24 | float3 dpdy = ddy(position); 25 | 26 | float dhdx = ddx(height); 27 | float dhdy = ddy(height); 28 | 29 | return PerturbNormal(normal, dpdx, dpdy, dhdx, dhdy); 30 | } 31 | 32 | float ApplyChainRule(float dhdu, float dhdv, float dud_, float dvd_) 33 | { 34 | return dhdu * dud_ + dhdv * dvd_; 35 | } 36 | 37 | // Calculate the surface normal using the uv-space gradient (dhdu, dhdv) 38 | // Requires height field gradient, double storage. 39 | float3 CalculateSurfaceNormal(float3 position, float3 normal, float2 gradient, float2 duvdx, float2 duvdy) 40 | { 41 | float3 dpdx = ddx(position); 42 | float3 dpdy = ddy(position); 43 | 44 | float dhdx = ApplyChainRule(gradient.x, gradient.y, duvdx.x, duvdx.y); 45 | float dhdy = ApplyChainRule(gradient.x, gradient.y, duvdy.x, duvdy.y); 46 | 47 | return PerturbNormal(normal, dpdx, dpdy, dhdx, dhdy); 48 | } 49 | 50 | // Returns the surface normal using screen-space partial derivatives of world position. 51 | // Will result in hard shading normals. 52 | float3 ComputeSurfaceNormal(float3 position) 53 | { 54 | return normalize(cross(ddx(position), ddy(position))); 55 | } 56 | 57 | // portability reasons 58 | float3 mul2x3(float2 val, float3 row1, float3 row2) 59 | { 60 | float3 res; 61 | for (int i = 0; i < 3; i++) 62 | { 63 | float2 col = float2(row1[i], row2[i]); 64 | res[i] = dot(val, col); 65 | } 66 | 67 | return res; 68 | } 69 | 70 | float3 ComputeSurfaceNormal(float3 normal, float3 tangent, float3 bitangent, sampler2D tex, float2 uv) 71 | { 72 | float3x3 tangentFrame = float3x3(normalize(bitangent), normalize(tangent), normal); 73 | 74 | #ifndef USE_FILTERING 75 | normal = UnpackNormal(tex2D(tex, uv)); 76 | #else 77 | float2 duv1 = ddx(uv) * 2; 78 | float2 duv2 = ddy(uv) * 2; 79 | normal = UnpackNormal(tex2Dgrad(tex, uv, duv1, duv2)); 80 | #endif 81 | return normalize(mul(normal, tangentFrame)); 82 | } 83 | 84 | float3x3 ComputeTangentFrame(float3 normal, float3 position, float2 uv) 85 | { 86 | float3 dp1 = ddx(position); 87 | float3 dp2 = ddy(position); 88 | float2 duv1 = ddx(uv); 89 | float2 duv2 = ddy(uv); 90 | 91 | float3x3 M = float3x3(dp1, dp2, cross(dp1, dp2)); 92 | float3 inverseM1 = float3(cross(M[1], M[2])); 93 | float3 inverseM2 = float3(cross(M[2], M[0])); 94 | float3 T = mul2x3(float2(duv1.x, duv2.x), inverseM1, inverseM2); 95 | float3 B = mul2x3(float2(duv1.y, duv2.y), inverseM1, inverseM2); 96 | 97 | return float3x3(normalize(T), normalize(B), normal); 98 | } 99 | 100 | // Returns the surface normal using screen-space partial derivatives of the uv and position coordinates. 101 | float3 ComputeSurfaceNormal(float3 normal, float3 position, sampler2D tex, float2 uv) 102 | { 103 | float3x3 tangentFrame = ComputeTangentFrame(normal, position, uv); 104 | 105 | #ifndef USE_FILTERING 106 | normal = UnpackNormal(tex2D(tex, uv)); 107 | #else 108 | float2 duv1 = ddx(uv) * 2; 109 | float2 duv2 = ddy(uv) * 2; 110 | normal = UnpackNormal(tex2Dgrad(tex, uv, duv1, duv2)); 111 | #endif 112 | return normalize(mul(normal, tangentFrame)); 113 | } 114 | 115 | float3 ComputeNormal(float4 heights, float strength) 116 | { 117 | float hL = heights.x; 118 | float hR = heights.y; 119 | float hD = heights.z; 120 | float hT = heights.w; 121 | 122 | float3 normal = float3(hL - hR, strength, hD - hT); 123 | return normalize(normal); 124 | } 125 | 126 | float3 ComputeNormal(sampler2D tex, float2 uv, float texelSize, float strength) 127 | { 128 | float3 off = float3(texelSize, texelSize, 0.0); 129 | float4 heights; 130 | heights.x = tex2D(tex, uv.xy - off.xz); // hL 131 | heights.y = tex2D(tex, uv.xy + off.xz); // hR 132 | heights.z = tex2D(tex, uv.xy - off.zy); // hD 133 | heights.w = tex2D(tex, uv.xy + off.zy); // hT 134 | 135 | return ComputeNormal(heights, strength); 136 | } 137 | -------------------------------------------------------------------------------- /shaders/hlsl/snoise.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : HLSL 2D simplex noise function 3 | // Author : Ian McEwan, Ashima Arts 4 | // Maintainer : ijm 5 | // Lastmod : 20110822 (ijm) 6 | // License : 7 | // Copyright (C) 2011 Ashima Arts. All rights reserved. 8 | // Distributed under the MIT License. See LICENSE file. 9 | // https://github.com/ashima/webgl-noise 10 | // 11 | 12 | float3 mod289(float3 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 13 | float2 mod289(float2 x) { return x - floor(x * (1.0 / 289.0)) * 289.0; } 14 | float3 permute(float3 x) { return mod289(((x*34.0) + 1.0)*x); } 15 | 16 | float snoise(float2 v) 17 | { 18 | // Precompute values for skewed triangular grid 19 | const float4 C = float4(0.211324865405187, // (3.0-sqrt(3.0))/6.0 20 | 0.366025403784439, // 0.5*(sqrt(3.0)-1.0) 21 | -0.577350269189626, // -1.0 + 2.0 * C.x 22 | 0.024390243902439 // 1.0 / 41.0 23 | ); 24 | 25 | 26 | // First corner (x0) 27 | float2 i = floor(v + dot(v, C.yy)); 28 | float2 x0 = v - i + dot(i, C.xx); 29 | 30 | // Other two corners (x1, x2) 31 | float2 i1; 32 | i1 = (x0.x > x0.y) ? float2(1.0, 0.0) : float2(0.0, 1.0); 33 | float2 x1 = x0.xy + C.xx - i1; 34 | float2 x2 = x0.xy + C.zz; 35 | 36 | // Do some permutations to avoid 37 | // truncation effects in permutation 38 | i = mod289(i); 39 | float3 p = permute( 40 | permute(i.y + float3(0.0, i1.y, 1.0)) 41 | + i.x + float3(0.0, i1.x, 1.0)); 42 | 43 | float3 m = max(0.5 - float3( 44 | dot(x0, x0), 45 | dot(x1, x1), 46 | dot(x2, x2) 47 | ), 0.0); 48 | 49 | m = m*m; 50 | m = m*m; 51 | 52 | // Gradients: 53 | // 41 pts uniformly over a line, mapped onto a diamond 54 | // The ring size 17*17 = 289 is close to a multiple of 41 (41*7 = 287) 55 | 56 | float3 x = 2.0 * frac(p * C.www) - 1.0; 57 | float3 h = abs(x) - 0.5; 58 | float3 ox = floor(x + 0.5); 59 | float3 a0 = x - ox; 60 | 61 | // Normalise gradients implicitly by scaling m 62 | // Approximation of: m *= inversesqrt(a0*a0 + h*h); 63 | m *= 1.79284291400159 - 0.85373472095314 *(a0*a0 + h*h); 64 | 65 | // Compute final noise value at P 66 | float3 g; 67 | g.x = a0.x * x0.x + h.x * x0.y; 68 | g.yz = a0.yz * float2(x1.x, x2.x) + h.yz * float2(x1.y, x2.y); 69 | return 130.0 * dot(m, g); 70 | } 71 | -------------------------------------------------------------------------------- /shaders/hlsl/unpack.cginc: -------------------------------------------------------------------------------- 1 | 2 | float3 UnpackNormal(float4 n) 3 | { 4 | n.xyz = n.xyz * 2.0 - 1.0; 5 | return n.xyz; 6 | } 7 | 8 | float3 UnpackNormalRecZ(float4 packednormal) 9 | { 10 | float3 normal; 11 | normal.xy = packednormal.wy * 2 - 1; 12 | normal.z = sqrt(1 - normal.x*normal.x - normal.y * normal.y); 13 | return normal; 14 | } 15 | -------------------------------------------------------------------------------- /shaders/hlsl/water.fx: -------------------------------------------------------------------------------- 1 | 2 | #include "unpack.cginc" 3 | #include "snoise.cginc" 4 | #include "bicubic.cginc" 5 | #include "normals.cginc" 6 | #include "water/displacement.cginc" 7 | #include "water/meansky.cginc" 8 | #include "water/radiance.cginc" 9 | #include "water/depth.cginc" 10 | #include "water/foam.cginc" 11 | 12 | uniform sampler2D _CameraDepthTexture; 13 | uniform sampler2D _NormalTexture; 14 | uniform sampler2D _FoamTexture; 15 | uniform sampler2D _ShoreTexture; 16 | uniform sampler2D _ReflectionTexture; 17 | uniform float4 _ReflectionTexture_TexelSize; 18 | uniform sampler2D _RefractionTexture; 19 | #ifdef USE_DISPLACEMENT 20 | uniform sampler2D _HeightTexture; 21 | #endif 22 | #ifdef USE_MEAN_SKY_RADIANCE 23 | uniform samplerCUBE _SkyTexture; 24 | #endif 25 | 26 | uniform float4 _WorldSpaceCameraPos; 27 | uniform float4 _WorldSpaceLightPos0; 28 | uniform float4x4 _ModelMatrix; 29 | uniform float4x4 _ModelMatrixInverse; 30 | uniform float4x4 _ViewProjectMatrix; 31 | uniform float4x4 _ViewProjectMatrixInverse; 32 | uniform float4x4 _ModelViewProjectMatrix; 33 | 34 | uniform float _Time; 35 | uniform float _AmbientDensity; 36 | uniform float _DiffuseDensity; 37 | uniform float _HeightIntensity; 38 | uniform float _NormalIntensity; 39 | uniform float _TextureTiling; 40 | 41 | uniform float4 _LightColor0; 42 | uniform float4 _AmbientColor; 43 | uniform float3 _SurfaceColor; 44 | uniform float3 _ShoreColor; 45 | uniform float3 _DepthColor; 46 | // Wind direction in world coordinates, amplitude encoded as the length of the vector 47 | uniform float2 _WindDirection; 48 | uniform float _WaveTiling; 49 | uniform float _WaveSteepness; 50 | uniform float _WaveAmplitudeFactor; 51 | // Displacement amplitude of multiple waves, x = smallest waves, w = largest waves 52 | uniform float4 _WaveAmplitude; 53 | // Intensity of multiple waves, affects the frequency of specific waves, x = smallest waves, w = largest waves 54 | uniform float4 _WavesIntensity; 55 | // Noise of multiple waves, x = smallest waves, w = largest waves 56 | uniform float4 _WavesNoise; 57 | // Affects how fast the colors will fade out, thus, use smaller values (eg. 0.05f). 58 | // to have crystal clear water and bigger to achieve "muddy" water. 59 | uniform float _WaterClarity; 60 | // Water transparency along eye vector 61 | uniform float _WaterTransparency; 62 | // Horizontal extinction of the RGB channels, in world coordinates. 63 | // Red wavelengths dissapear(get absorbed) at around 5m, followed by green(75m) and blue(300m). 64 | uniform float3 _HorizontalExtinction; 65 | uniform float _Shininess; 66 | // xy = Specular intensity values, z = shininess exponential factor. 67 | uniform float3 _SpecularValues; 68 | // x = index of refraction constant, y = refraction intensity 69 | // if you want to empasize reflections use values smaller than 0 for refraction intensity. 70 | uniform float2 _RefractionValues; 71 | // Amount of wave refraction, of zero then no refraction. 72 | uniform float _RefractionScale; 73 | // Reflective radiance factor. 74 | uniform float _RadianceFactor; 75 | // Reflection distortion, the higher the more distortion. 76 | uniform float _Distortion; 77 | // x = range for shore foam, y = range for near shore foam, z = threshold for wave foam 78 | uniform float3 _FoamRanges; 79 | // x = noise for shore, y = noise for outer 80 | // z = speed of the noise for shore, y = speed of the noise for outer, not that speed can be negative 81 | uniform float4 _FoamNoise; 82 | uniform float2 _FoamTiling; 83 | // Extra speed applied to the wind speed near the shore 84 | uniform float _FoamSpeed; 85 | uniform float _FoamIntensity; 86 | uniform float _ShoreFade; 87 | 88 | struct VertexInput { 89 | float4 vertex : POSITION; 90 | }; 91 | struct VertexOutput { 92 | float4 pos : SV_POSITION; 93 | float2 uv : TEXCOORD0; 94 | float3 normal : TEXCOORD1; // world normal 95 | float3 tangent : TEXCOORD2; 96 | float3 bitangent : TEXCOORD3; 97 | float3 worldPos : TEXCOORD4; 98 | float4 projPos : TEXCOORD5; 99 | float timer : TEXCOORD6; 100 | float4 wind : TEXCOORD7; // xy = normalized wind, zw = wind multiplied with timer 101 | }; 102 | 103 | float4 ClipToScreenPos(float4 pos) 104 | { 105 | float4 o = pos * 0.5f; 106 | o.xy += o.w; 107 | o.zw = pos.zw; 108 | return o; 109 | } 110 | 111 | VertexOutput vert(VertexInput v) 112 | { 113 | VertexOutput o = (VertexOutput)0; 114 | 115 | float2 windDir = _WindDirection; 116 | float windSpeed = length(_WindDirection); 117 | windDir /= windSpeed; 118 | float timer = _Time * windSpeed * 10; 119 | 120 | float4 modelPos = v.vertex; 121 | float3 worldPos = mul(_ModelMatrix, float4(modelPos.xyz, 1)); 122 | half3 normal = half3(0, 1, 0); 123 | 124 | #ifdef USE_DISPLACEMENT 125 | float cameraDistance = length(_WorldSpaceCameraPos.xyz - worldPos); 126 | float2 noise = GetNoise(worldPos.xz, timer * windDir * 0.5); 127 | 128 | half3 tangent; 129 | float4 waveSettings = float4(windDir, _WaveSteepness, _WaveTiling); 130 | float4 waveAmplitudes = _WaveAmplitude * _WaveAmplitudeFactor; 131 | worldPos = ComputeDisplacement(worldPos, cameraDistance, noise, timer, 132 | waveSettings, waveAmplitudes, _WavesIntensity, _WavesNoise, 133 | normal, tangent); 134 | 135 | // add extra noise height from a heightmap 136 | float heightIntensity = _HeightIntensity * (1.0 - cameraDistance / 100.0) * _WaveAmplitude; 137 | float2 texCoord = worldPos.xz * 0.05 *_TextureTiling; 138 | if (heightIntensity > 0.02) 139 | { 140 | float height = ComputeNoiseHeight(_HeightTexture, _WavesIntensity, _WavesNoise, 141 | texCoord, noise, timer); 142 | worldPos.y += height * heightIntensity; 143 | } 144 | 145 | modelPos = mul(_ModelMatrixInverse, float4(worldPos, 1.0)); 146 | o.tangent = tangent; 147 | o.bitangent = cross(normal, tangent); 148 | #endif 149 | float2 uv = worldPos.xz; 150 | 151 | o.timer = timer; 152 | o.wind.xy = windDir; 153 | o.wind.zw = windDir * timer; 154 | 155 | o.uv = uv * 0.05 * _TextureTiling; 156 | o.pos = mul(_ModelViewProjectMatrix, float4(modelPos.xyz, 1.0)); 157 | o.worldPos = worldPos; 158 | o.projPos = ClipToScreenPos(o.pos); 159 | o.normal = normal; 160 | 161 | return o; 162 | } 163 | 164 | float4 NdcToClipPos(float3 ndc) 165 | { 166 | // map xy to -1,1 167 | float4 clipPos = float4(ndc.xy * 2.0f - 1.0f, ndc.z, 1.0f); 168 | 169 | #if defined(REVERSED_Z) 170 | //D3d with reversed Z 171 | clipPos.z = 1.0f - clipPos.z; 172 | #endif 173 | 174 | return clipPos; 175 | } 176 | 177 | float3 NdcToWorldPos(float4x4 inverseVP, float3 ndc) 178 | { 179 | float4 clipPos = NdcToClipPos(ndc); 180 | float4 pos = mul(inverseVP, clipPos); 181 | pos.xyz /= pos.w; 182 | 183 | return pos.xyz; 184 | } 185 | 186 | float4 frag(VertexOutput fs_in, float facing : VFACE) : COLOR 187 | { 188 | float timer = fs_in.timer; 189 | float2 windDir = fs_in.wind.xy; 190 | float2 timedWindDir = fs_in.wind.zw; 191 | float2 ndcPos = float2(fs_in.projPos.xy / fs_in.projPos.w); 192 | float3 eyeDir = normalize(_WorldSpaceCameraPos.xyz - fs_in.worldPos); 193 | float3 surfacePosition = fs_in.worldPos; 194 | half3 lightColor = _LightColor0.rgb; 195 | 196 | //wave normal 197 | #ifdef USE_DISPLACEMENT 198 | half3 normal = ComputeNormal(_NormalTexture, surfacePosition.xz, fs_in.uv, 199 | fs_in.normal, fs_in.tangent, fs_in.bitangent, _WavesNoise, _WavesIntensity, timedWindDir); 200 | #else 201 | half3 normal = ComputeNormal(_NormalTexture, surfacePosition.xz, fs_in.uv, 202 | fs_in.normal, 0, 0, _WavesNoise, _WavesIntensity, timedWindDir); 203 | #endif 204 | normal = normalize(lerp(fs_in.normal, normalize(normal), _NormalIntensity)); 205 | 206 | // compute refracted color 207 | float depth = tex2Dproj(_CameraDepthTexture, fs_in.projPos.xyww); 208 | float3 depthPosition = NdcToWorldPos(_ViewProjectMatrixInverse, float3(ndcPos, depth)); 209 | float waterDepth = surfacePosition.y - depthPosition.y; // horizontal water depth 210 | float viewWaterDepth = length(surfacePosition - depthPosition); // water depth from the view direction(water accumulation) 211 | float2 dudv = ndcPos; 212 | { 213 | // refraction based on water depth 214 | float refractionScale = _RefractionScale * min(waterDepth, 1.0f); 215 | float2 delta = float2(sin(timer + 3.0f * abs(depthPosition.y)), 216 | sin(timer + 5.0f * abs(depthPosition.y))); 217 | dudv += windDir * delta * refractionScale; 218 | } 219 | half3 pureRefractionColor = tex2D(_RefractionTexture, dudv).rgb; 220 | float2 waterTransparency = float2(_WaterClarity, _WaterTransparency); 221 | float2 waterDepthValues = float2(waterDepth, viewWaterDepth); 222 | float shoreRange = max(_FoamRanges.x, _FoamRanges.y) * 2.0; 223 | half3 refractionColor = DepthRefraction(waterTransparency, waterDepthValues, shoreRange, _HorizontalExtinction, 224 | pureRefractionColor, _ShoreColor, _SurfaceColor, _DepthColor); 225 | 226 | // compute ligths's reflected radiance 227 | float3 lightDir = normalize(_WorldSpaceLightPos0); 228 | half fresnel = FresnelValue(_RefractionValues, normal, eyeDir); 229 | half3 specularColor = ReflectedRadiance(_Shininess, _SpecularValues, lightColor, lightDir, eyeDir, normal, fresnel); 230 | 231 | // compute sky's reflected radiance 232 | #ifdef USE_MEAN_SKY_RADIANCE 233 | half3 reflectColor = fresnel * MeanSkyRadiance(_SkyTexture, eyeDir, normal) * _RadianceFactor; 234 | #else 235 | half3 reflectColor = 0; 236 | #endif // #ifndef USE_MEAN_SKY_RADIANCE 237 | 238 | // compute reflected color 239 | dudv = ndcPos + _Distortion * normal.xz; 240 | #ifdef USE_FILTERING 241 | reflectColor += tex2DBicubic(_ReflectionTexture, _ReflectionTexture_TexelSize.z, dudv).rgb; 242 | #else 243 | reflectColor += tex2D(_ReflectionTexture, dudv).rgb; 244 | #endif // #ifdef USE_FILTERING 245 | 246 | // shore foam 247 | #ifdef USE_FOAM 248 | float maxAmplitude = max(max(_WaveAmplitude.x, _WaveAmplitude.y), _WaveAmplitude.z); 249 | half foam = FoamValue(_ShoreTexture, _FoamTexture, _FoamTiling, 250 | _FoamNoise, _FoamSpeed * windDir, _FoamRanges, maxAmplitude, 251 | surfacePosition, depthPosition, eyeDir, waterDepth, timedWindDir, timer); 252 | foam *= _FoamIntensity; 253 | #else 254 | half foam = 0; 255 | #endif // #ifdef USE_FOAM 256 | 257 | half shoreFade = saturate(waterDepth * _ShoreFade); 258 | // ambient + diffuse 259 | half3 ambientColor = _AmbientColor.rgb * _AmbientDensity + saturate(dot(normal, lightDir)) * _DiffuseDensity; 260 | // refraction color with depth based color 261 | pureRefractionColor = lerp(pureRefractionColor, reflectColor, fresnel * saturate(waterDepth / (_FoamRanges.x * 0.4))); 262 | pureRefractionColor = lerp(pureRefractionColor, _ShoreColor, 0.30 * shoreFade); 263 | // compute final color 264 | half3 color = lerp(refractionColor, reflectColor, fresnel); 265 | color = saturate(ambientColor + color + max(specularColor, foam * lightColor)); 266 | color = lerp(pureRefractionColor + specularColor * shoreFade, color, shoreFade); 267 | 268 | #ifdef DEBUG_NORMALS 269 | color.rgb = 0.5 + 2 * ambientColor + specularColor + clamp(dot(normal, lightDir), 0, 1) * 0.5; 270 | #endif 271 | 272 | return float4(color, 1.0); 273 | } 274 | -------------------------------------------------------------------------------- /shaders/hlsl/water/depth.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Water color based on water depth and color extinction 3 | // 4 | // based on Rendering Water as a Post-process Effect by Wojciech Toman 5 | // 6 | 7 | // waterTransparency - x = , y = water visibility along eye vector, 8 | // waterDepthValues - x = water depth in world space, y = view/accumulated water depth in world space 9 | half3 DepthRefraction(float2 waterTransparency, float2 waterDepthValues, float shoreRange, float3 horizontalExtinction, 10 | half3 refractionColor, half3 shoreColor, half3 surfaceColor, half3 depthColor) 11 | { 12 | float waterClarity = waterTransparency.x; 13 | float visibility = waterTransparency.y; 14 | float waterDepth = waterDepthValues.x; 15 | float viewWaterDepth = waterDepthValues.y; 16 | 17 | float accDepth = viewWaterDepth * waterClarity; // accumulated water depth 18 | float accDepthExp = saturate(accDepth / (2.5 * visibility)); 19 | accDepthExp *= (1.0 - accDepthExp) * accDepthExp * accDepthExp + 1; // out cubic 20 | 21 | surfaceColor = lerp(shoreColor, surfaceColor, saturate(waterDepth / shoreRange)); 22 | half3 waterColor = lerp(surfaceColor, depthColor, saturate(waterDepth / horizontalExtinction)); 23 | 24 | refractionColor = lerp(refractionColor, surfaceColor * waterColor, saturate(accDepth / visibility)); 25 | refractionColor = lerp(refractionColor, depthColor, accDepthExp); 26 | refractionColor = lerp(refractionColor, depthColor * waterColor, saturate(waterDepth / horizontalExtinction)); 27 | return refractionColor; 28 | } -------------------------------------------------------------------------------- /shaders/hlsl/water/displacement.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Utilties for waves displacement 3 | // 4 | 5 | #include "waves.cginc" 6 | 7 | float2 GetNoise(in float2 position, in float2 timedWindDir) 8 | { 9 | float2 noise; 10 | noise.x = snoise(position * 0.015 + timedWindDir * 0.0005); // large and slower noise 11 | noise.y = snoise(position * 0.1 + timedWindDir * 0.002); // smaller and faster noise 12 | return saturate(noise); 13 | } 14 | 15 | void AdjustWavesValues(in float2 noise, inout half4 wavesNoise, inout half4 wavesIntensity) 16 | { 17 | wavesNoise = wavesNoise * half4(noise.y * 0.25, noise.y * 0.25, noise.x + noise.y, noise.y); 18 | wavesIntensity = wavesIntensity + half4(saturate(noise.y - noise.x), noise.x, noise.y, noise.x + noise.y); 19 | wavesIntensity = clamp(wavesIntensity, 0.01, 10); 20 | } 21 | 22 | // uv in texture space, normal in world space 23 | half3 ComputeNormal(sampler2D normalTexture, float2 worldPos, float2 texCoord, 24 | half3 normal, half3 tangent, half3 bitangent, 25 | half4 wavesNoise, half4 wavesIntensity, float2 timedWindDir) 26 | { 27 | float2 noise = GetNoise(worldPos, timedWindDir * 0.5); 28 | AdjustWavesValues(noise, wavesNoise, wavesIntensity); 29 | 30 | float2 texCoords[4] = { texCoord * 1.6 + timedWindDir * 0.064 + wavesNoise.x, 31 | texCoord * 0.8 + timedWindDir * 0.032 + wavesNoise.y, 32 | texCoord * 0.5 + timedWindDir * 0.016 + wavesNoise.z, 33 | texCoord * 0.3 + timedWindDir * 0.008 + wavesNoise.w }; 34 | 35 | half3 wavesNormal = half3(0, 1, 0); 36 | #ifdef USE_DISPLACEMENT 37 | normal = normalize(normal); 38 | tangent = normalize(tangent); 39 | bitangent = normalize(bitangent); 40 | for (int i = 0; i < 4; ++i) 41 | { 42 | wavesNormal += ComputeSurfaceNormal(normal, tangent, bitangent, normalTexture, texCoords[i]) * wavesIntensity[i]; 43 | } 44 | #else 45 | for (int i = 0; i < 4; ++i) 46 | { 47 | wavesNormal += UnpackNormal(tex2D(normalTexture, texCoords[i])) * wavesIntensity[i]; 48 | } 49 | wavesNormal.xyz = wavesNormal.xzy; // flip zy to avoid btn multiplication 50 | #endif // #ifdef USE_DISPLACEMENT 51 | 52 | return wavesNormal; 53 | } 54 | 55 | #ifdef USE_DISPLACEMENT 56 | float ComputeNoiseHeight(sampler2D heightTexture, float4 wavesIntensity, float4 wavesNoise, float2 texCoord, float2 noise, float2 timedWindDir) 57 | { 58 | AdjustWavesValues(noise, wavesNoise, wavesIntensity); 59 | 60 | float2 texCoords[4] = { texCoord * 1.6 + timedWindDir * 0.064 + wavesNoise.x, 61 | texCoord * 0.8 + timedWindDir * 0.032 + wavesNoise.y, 62 | texCoord * 0.5 + timedWindDir * 0.016 + wavesNoise.z, 63 | texCoord * 0.3 + timedWindDir * 0.008 + wavesNoise.w }; 64 | float height = 0; 65 | for (int i = 0; i < 4; ++i) 66 | { 67 | height += tex2Dlod(heightTexture, float4(texCoords[i], 0, 0)).x * wavesIntensity[i]; 68 | } 69 | 70 | return height; 71 | } 72 | 73 | float3 ComputeDisplacement(float3 worldPos, float cameraDistance, float2 noise, float timer, 74 | float4 waveSettings, float4 waveAmplitudes, float4 wavesIntensity, float4 waveNoise, 75 | out half3 normal, out half3 tangent) 76 | { 77 | float2 windDir = waveSettings.xy; 78 | float waveSteepness = waveSettings.z; 79 | float waveTiling = waveSettings.w; 80 | 81 | //TODO: improve motion/simulation instead of just noise 82 | //TODO: fix UV due to wave distortion 83 | 84 | wavesIntensity = normalize(wavesIntensity); 85 | waveNoise = half4(noise.x - noise.x * 0.2 + noise.y * 0.1, noise.x + noise.y * 0.5 - noise.y * 0.1, noise.x, noise.x) * waveNoise; 86 | half4 wavelengths = half4(1, 4, 3, 6) + waveNoise; 87 | half4 amplitudes = waveAmplitudes + half4(0.5, 1, 4, 1.5) * waveNoise; 88 | 89 | // reduce wave intensity base on distance to reduce aliasing 90 | wavesIntensity *= 1.0 - saturate(half4(cameraDistance / 120.0, cameraDistance / 150.0, cameraDistance / 170.0, cameraDistance / 400.0)); 91 | 92 | // compute position and normal from several sine and gerstner waves 93 | tangent = normal = half3(0, 1, 0); 94 | float2 timers = float2(timer * 0.5, timer * 0.25); 95 | for (int i = 2; i < 4; ++i) 96 | { 97 | float A = wavesIntensity[i] * amplitudes[i]; 98 | float3 vals = SineWaveValues(worldPos.xz * waveTiling, windDir, A, wavelengths[i], timer); 99 | normal += wavesIntensity[i] * SineWaveNormal(windDir, A, vals); 100 | tangent += wavesIntensity[i] * SineWaveTangent(windDir, A, vals); 101 | worldPos.y += SineWaveDelta(A, vals); 102 | } 103 | 104 | // using normalized wave steepness, tranform to Q 105 | float2 Q = waveSteepness / ((2 * 3.14159265 / wavelengths.xy) * amplitudes.xy); 106 | for (int j = 0; j < 2; ++j) 107 | { 108 | float A = wavesIntensity[j] * amplitudes[j]; 109 | float3 vals = GerstnerWaveValues(worldPos.xz * waveTiling, windDir, A, wavelengths[j], Q[j], timer); 110 | normal += wavesIntensity[j] * GerstnerWaveNormal(windDir, A, Q[j], vals); 111 | tangent += wavesIntensity[j] * GerstnerWaveTangent(windDir, A, Q[j], vals); 112 | worldPos += GerstnerWaveDelta(windDir, A, Q[j], vals); 113 | } 114 | 115 | normal = normalize(normal); 116 | tangent = normalize(tangent); 117 | if (length(wavesIntensity) < 0.01) 118 | { 119 | normal = half3(0, 1, 0); 120 | tangent = half3(0, 0, 1); 121 | } 122 | 123 | return worldPos; 124 | } 125 | #endif 126 | -------------------------------------------------------------------------------- /shaders/hlsl/water/foam.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Foam color based on water depth, near the shore 3 | // 4 | 5 | half FoamColor(sampler2D tex, float2 texCoord, float2 texCoord2, float2 ranges, half2 factors, float waterDepth, half baseColor) 6 | { 7 | float f1 = tex2D(tex, texCoord).r; 8 | float f2 = tex2D(tex, texCoord2).r; 9 | return lerp(f1 * factors.x + f2 * factors.y, baseColor, smoothstep(ranges.x, ranges.y, waterDepth)); 10 | } 11 | 12 | // surfacePosition, depthPosition, eyeVec in world space 13 | // waterDepth is the horizontal water depth in world space 14 | half FoamValue(sampler2D shoreTexture, sampler2D foamTexture, float2 foamTiling, 15 | float4 foamNoise, float2 foamSpeed, float3 foamRanges, float maxAmplitude, 16 | float3 surfacePosition, float3 depthPosition, float3 eyeVec, float waterDepth, 17 | float2 timedWindDir, float timer) 18 | { 19 | float2 position = (surfacePosition.xz + eyeVec.xz * 0.1) * 0.5; 20 | 21 | float s = sin(timer * 0.01 + depthPosition.x); 22 | float2 texCoord = position + timer * 0.01 * foamSpeed + s * 0.05; 23 | s = sin(timer * 0.01 + depthPosition.z); 24 | float2 texCoord2 = (position + timer * 0.015 * foamSpeed + s * 0.05) * -0.5; // also flip 25 | float2 texCoord3 = texCoord * foamTiling.x; 26 | float2 texCoord4 = (position + timer * 0.015 * -foamSpeed * 0.3 + s * 0.05) * -0.5 * foamTiling.x; // reverse direction 27 | texCoord *= foamTiling.y; 28 | texCoord2 *= foamTiling.y; 29 | 30 | float2 ranges = foamRanges.xy; 31 | ranges.x += snoise(surfacePosition.xz + foamNoise.z * timedWindDir) * foamNoise.x; 32 | ranges.y += snoise(surfacePosition.xz + foamNoise.w * timedWindDir) * foamNoise.y; 33 | ranges = clamp(ranges, 0.0, 10.0); 34 | 35 | float foamEdge = max(ranges.x, ranges.y); 36 | half deepFoam = FoamColor(foamTexture, texCoord, texCoord2, float2(ranges.x, foamEdge), half2(1.0, 0.5), waterDepth, 0.0); 37 | half foam = FoamColor(shoreTexture, texCoord3 * 0.25, texCoord4, float2(0.0, ranges.x), half2(0.75, 1.5), waterDepth, deepFoam); 38 | 39 | // high waves foam 40 | if (surfacePosition.y - foamRanges.z > 0.0001f) 41 | { 42 | half amount = saturate((surfacePosition.y - foamRanges.z) / maxAmplitude) * 0.25; 43 | foam += (tex2D(shoreTexture, texCoord3).x + tex2D(shoreTexture, texCoord4).x * 0.5f) * amount; 44 | } 45 | 46 | return foam; 47 | } 48 | -------------------------------------------------------------------------------- /shaders/hlsl/water/meansky.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Mean sky radiance 3 | // 4 | // based on Real-time Realistic Ocean Lighting using Seamless Transitions from Geometry to BRDF by Eric Bruneton 5 | // 6 | 7 | #ifdef USE_MEAN_SKY_RADIANCE 8 | 9 | // V, N, Tx, Ty in world space 10 | float2 U(float2 zeta, float3 V, float3 N, float3 Tx, float3 Ty) 11 | { 12 | float3 f = normalize(float3(-zeta, 1.0)); // tangent space 13 | float3 F = f.x * Tx + f.y * Ty + f.z * N; // world space 14 | float3 R = 2.0 * dot(F, V) * F - V; 15 | return dot(F, V); 16 | } 17 | 18 | // viewDir and normal in world space 19 | half3 MeanSkyRadiance(samplerCUBE skyTexture, float3 viewDir, half3 normal) 20 | { 21 | if (dot(viewDir, normal) < 0.0) 22 | { 23 | normal = reflect(normal, viewDir); 24 | } 25 | float3 ty = normalize(float3(0.0, normal.z, -normal.y)); 26 | float3 tx = cross(ty, normal); 27 | 28 | const float eps = 0.001; 29 | float2 u0 = U(float2(0, 0), viewDir, normal, tx, ty) * 0.05; 30 | float2 dux = 2.0 * (float2(eps, 0.0) - u0) / eps; 31 | float2 duy = 2.0 * (float2(0, eps) - u0) / eps; 32 | return texCUBE(skyTexture, float3(u0.xy, 1.0)).rgb; //TODO: transform hemispherical cordinates to cube or use a 2d texture 33 | } 34 | 35 | #endif // #ifdef USE_MEAN_SKY_RADIANCE 36 | -------------------------------------------------------------------------------- /shaders/hlsl/water/radiance.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Reflected water radiance 3 | // 4 | 5 | // refractionValues, x = index of refraction constant, y = refraction strength 6 | // normal and eyeVec in world space 7 | half FresnelValue(float2 refractionValues, float3 normal, float3 eyeVec) 8 | { 9 | // R0 is a constant related to the index of refraction (IOR). 10 | float R0 = refractionValues.x; 11 | // This value modifies current fresnel term. If you want to weaken 12 | // reflections use bigger value. 13 | float refractionStrength = refractionValues.y; 14 | #ifdef SIMPLIFIED_FRESNEL 15 | return R0 + (1.0f - R0) * pow(1.0f - dot(eyeVec, normal), 5.0f); 16 | #else 17 | float angle = 1.0f - saturate(dot(normal, eyeVec)); 18 | float fresnel = angle * angle; 19 | fresnel *= fresnel; 20 | fresnel *= angle; 21 | return saturate(fresnel * (1.0f - saturate(R0)) + R0 - refractionStrength); 22 | #endif // #ifdef SIMPLIFIED_FRESNEL 23 | } 24 | 25 | // lightDir, eyeDir and normal in world space 26 | half3 ReflectedRadiance(float shininess, half3 specularValues, half3 lightColor, float3 lightDir, float3 eyeDir, float3 normal, float fresnel) 27 | { 28 | float shininessExp = specularValues.z; 29 | 30 | #ifdef BLINN_PHONG 31 | // a variant of the blinn phong shading 32 | float specularIntensity = specularValues.x * 0.0075; 33 | 34 | float3 H = normalize(eyeDir + lightDir); 35 | float e = shininess * shininessExp * 800; 36 | float kS = saturate(dot(normal, lightDir)); 37 | half3 specular = kS * specularIntensity * pow(saturate(dot(normal, H)), e) * sqrt((e + 1) / 2); 38 | specular *= lightColor; 39 | #else 40 | float2 specularIntensity = specularValues.xy; 41 | // reflect the eye vector such that the incident and emergent angles are equal 42 | float3 mirrorEye = reflect(-eyeDir, normal); 43 | half dotSpec = saturate(dot(mirrorEye, lightDir) * 0.5f + 0.5f); 44 | half3 specular = (1.0f - fresnel) * saturate(lightDir.y) * pow(dotSpec, specularIntensity.y) * (shininess * shininessExp + 0.2f) * lightColor; 45 | specular += specular * specularIntensity.x * saturate(shininess - 0.05f) * lightColor; 46 | #endif // #ifdef BLINN_PHONG 47 | return specular; 48 | } 49 | -------------------------------------------------------------------------------- /shaders/hlsl/water/waves.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Utilties for waves simulation 3 | // 4 | // based on GPU Gems Chapter 1. Effective Water Simulation from Physical Models, by Mark Finch and Cyan Worlds 5 | // 6 | 7 | float3 GerstnerWaveValues(float2 position, float2 D, float amplitude, float wavelength, float Q, float timer) 8 | { 9 | float w = 2 * 3.14159265 / wavelength; 10 | float dotD = dot(position, D); 11 | float v = w * dotD + timer; 12 | return float3(cos(v), sin(v), w); 13 | } 14 | 15 | half3 GerstnerWaveNormal(float2 D, float A, float Q, float3 vals) 16 | { 17 | half C = vals.x; 18 | half S = vals.y; 19 | half w = vals.z; 20 | half WA = w * A; 21 | half WAC = WA * C; 22 | half3 normal = half3(-D.x * WAC, 1.0 - Q * WA * S, -D.y * WAC); 23 | return normalize(normal); 24 | } 25 | 26 | half3 GerstnerWaveTangent(float2 D, float A, float Q, float3 vals) 27 | { 28 | half C = vals.x; 29 | half S = vals.y; 30 | half w = vals.z; 31 | half WA = w * A; 32 | half WAS = WA * S; 33 | half3 normal = half3(Q * -D.x * D.y * WAS, D.y * WA * C, 1.0 - Q * D.y * D.y * WAS); 34 | return normalize(normal); 35 | } 36 | 37 | float3 GerstnerWaveDelta(float2 D, float A, float Q, float3 vals) 38 | { 39 | float C = vals.x; 40 | float S = vals.y; 41 | float QAC = Q * A * C; 42 | return float3(QAC * D.x, A * S, QAC * D.y); 43 | } 44 | 45 | void GerstnerWave(float2 windDir, float tiling, float amplitude, float wavelength, float Q, float timer, inout float3 position, out half3 normal) 46 | { 47 | float2 D = windDir; 48 | float3 vals = GerstnerWaveValues(position.xz * tiling, D, amplitude, wavelength, Q, timer); 49 | normal = GerstnerWaveNormal(D, amplitude, Q, vals); 50 | position += GerstnerWaveDelta(D, amplitude, Q, vals); 51 | } 52 | 53 | float3 SineWaveValues(float2 position, float2 D, float amplitude, float wavelength, float timer) 54 | { 55 | float w = 2 * 3.14159265 / wavelength; 56 | float dotD = dot(position, D); 57 | float v = w * dotD + timer; 58 | return float3(cos(v), sin(v), w); 59 | } 60 | 61 | half3 SineWaveNormal(float2 D, float A, float3 vals) 62 | { 63 | half C = vals.x; 64 | half w = vals.z; 65 | half WA = w * A; 66 | half WAC = WA * C; 67 | half3 normal = half3(-D.x * WAC, 1.0, -D.y * WAC); 68 | return normalize(normal); 69 | } 70 | 71 | half3 SineWaveTangent(float2 D, float A, float3 vals) 72 | { 73 | half C = vals.x; 74 | half w = vals.z; 75 | half WAC = w * A * C; 76 | half3 normal = half3(0.0, D.y * WAC, 1.0); 77 | return normalize(normal); 78 | } 79 | 80 | float SineWaveDelta(float A, float3 vals) 81 | { 82 | return vals.y * A; 83 | } 84 | 85 | void SineWave(float2 windDir, float tiling, float amplitude, float wavelength, float timer, inout float3 position, out half3 normal) 86 | { 87 | float2 D = windDir; 88 | float3 vals = SineWaveValues(position.xz * tiling, D, amplitude, wavelength, timer); 89 | normal = SineWaveNormal(D, amplitude, vals); 90 | position.y += SineWaveDelta(amplitude, vals); 91 | } 92 | -------------------------------------------------------------------------------- /shaders/unity/conversion.cginc: -------------------------------------------------------------------------------- 1 | // 2 | // Description : Utilties for NDC to world/clip coordinates and other 3 | // 4 | 5 | #define INVERSE_FOG_COLOR(fogCoord, color) UNITY_CALC_FOG_FACTOR((fogCoord)); color = yInverseLerp(unity_FogColor, color, saturate(unityFogFactor)) 6 | 7 | float4 NdcToClipPos(float3 ndc) 8 | { 9 | // map xy to -1,1 10 | float4 clipPos = float4(ndc.xy * 2.0f - 1.0f, ndc.z, 1.0f); 11 | 12 | #if defined(UNITY_REVERSED_Z) 13 | //D3d with reversed Z 14 | clipPos.z = 1.0f - clipPos.z; 15 | #elif UNITY_UV_STARTS_AT_TOP 16 | //D3d without reversed z 17 | #else 18 | //opengl, map to -1,1 19 | clipPos.z = clipPos.z * 2.0f - 1.0f; 20 | #endif 21 | 22 | return clipPos; 23 | } 24 | 25 | float3 NdcToWorldPos(float4x4 inverseVP, float3 ndc) 26 | { 27 | float4 clipPos = NdcToClipPos(ndc); 28 | float4 pos = mul(inverseVP, clipPos); 29 | pos.xyz /= pos.w; 30 | 31 | return pos.xyz; 32 | } 33 | 34 | float3 yInverseLerp(float3 x, float3 y, float a) 35 | { 36 | if (a) 37 | return (y - x * (1 - a)) / a; 38 | return y; 39 | } 40 | -------------------------------------------------------------------------------- /shaders/unity/water.shader: -------------------------------------------------------------------------------- 1 |  2 | Shader "Water" { 3 | Properties{ 4 | [Header(Features)] 5 | [Toggle(USE_DISPLACEMENT)] _UseDisplacement("Displacement", Float) = 0 6 | [Toggle(USE_MEAN_SKY_RADIANCE)] _UseMeanSky("Mean sky radiance", Float) = 0 7 | [Toggle(USE_FILTERING)] _UseFiltering("Filtering", Float) = 0 8 | [Toggle(USE_FOAM)] _UseFoam("Foam", Float) = 0 9 | [Toggle(BLINN_PHONG)] _UsePhong("Blinn Phong", Float) = 0 10 | 11 | [Header(Basic settings)] 12 | _AmbientDensity("Ambient Intensity", Range(0, 1)) = 0.15 13 | _DiffuseDensity("Diffuse Intensity", Range(0, 1)) = 0.1 14 | _SurfaceColor("Surface Color", Color) = (0.0078, 0.5176, 0.7) 15 | _ShoreColor("Shore Tint Color", Color) = (0.0078, 0.5176, 0.7) 16 | _DepthColor("Deep Color", Color) = (0.0039, 0.00196, 0.145) 17 | [NoScaleOffset]_SkyTexture("Sky Texture", Cube) = "white" {} 18 | [NoScaleOffset]_NormalTexture("Normal Texture", 2D) = "white" {} 19 | _NormalIntensity("Normal Intensity", Range(0, 1)) = 0.5 20 | _TextureTiling("Texture Tiling", Float) = 1 21 | _WindDirection("Wind Direction", Vector) = (3,5,0) 22 | 23 | [Header(Displacement settings)] 24 | [NoScaleOffset]_HeightTexture("Height Texture", 2D) = "white" {} 25 | _HeightIntensity("Height Intensity", Range(0, 1)) = 0.5 26 | _WaveTiling("Wave Tiling", Float) = 1 27 | _WaveAmplitudeFactor("Wave Amplitude Factor",Float) = 1.0 28 | _WaveSteepness("Wave Steepness", Range(0, 1)) = 0.5 29 | _WaveAmplitude("Waves Amplitude", Vector) = (0.05, 0.1, 0.2, 0.3) 30 | _WavesIntensity("Waves Intensity", Vector) = (3, 2, 2, 10) 31 | _WavesNoise("Waves Noise", Vector) = (0.05, 0.15, 0.03, 0.05) 32 | 33 | [Header(Refraction settings)] 34 | _WaterClarity("Water Clarity", Range(0, 3)) = 0.75 35 | _WaterTransparency("Water Transparency", Range(0, 30)) = 10.0 36 | _HorizontalExtinction("Horizontal Extinction", Vector) = (3.0, 10.0, 12.0) 37 | _RefractionValues("Refraction/Reflection", Vector) = (0.3, 0.01, 1.0) 38 | _RefractionScale("Refraction Scale", Range(0, 0.03)) = 0.005 39 | 40 | [Header(Reflection settings)] 41 | _Shininess("Shininess", Range(0, 3)) = 0.5 42 | _SpecularValues("Specular Intensity", Vector) = (12, 768, 0.15) 43 | _Distortion("Distortion", Range(0, 0.15)) = 0.05 44 | _RadianceFactor("Radiance Factor", Range(0, 1.0)) = 1.0 45 | [HideInInspector]_ReflectionTexture("Reflection Texture", 2D) = "white" {} 46 | 47 | [Header(Foam settings)] 48 | [NoScaleOffset]_FoamTexture("Foam Texture", 2D) = "white" {} 49 | [NoScaleOffset]_ShoreTexture("Shore Texture", 2D) = "white" {} 50 | _FoamTiling("Foam Tiling", Vector) = (2.0, 0.5, 0.0) 51 | _FoamRanges("Foam Ranges", Vector) = (2.0, 3.0, 100.0) 52 | _FoamNoise("Foam Noise", Vector) = (0.1, 0.3, 0.1, 0.3) 53 | _FoamSpeed("Foam Speed", Float) = 10 54 | _FoamIntensity("Foam Intensity", Range(0, 1)) = 0.5 55 | _ShoreFade("Shore Fade", Range(0.1, 3)) = 0.3 56 | } 57 | SubShader{ 58 | Tags{ 59 | "IgnoreProjector" = "True" 60 | "Queue" = "Transparent" 61 | "RenderType" = "Transparent" 62 | } 63 | GrabPass{ "_RefractionTexture" } 64 | Pass{ 65 | Name "Base" 66 | Tags{ "LightMode" = "ForwardBase" } 67 | Blend SrcAlpha OneMinusSrcAlpha 68 | Cull False 69 | ZWrite True 70 | 71 | CGPROGRAM 72 | #pragma vertex vert 73 | #pragma fragment frag 74 | #include "UnityCG.cginc" 75 | #include "conversion.cginc" 76 | #include "hlsl/snoise.cginc" 77 | #include "hlsl/bicubic.cginc" 78 | #include "hlsl/normals.cginc" 79 | #include "hlsl/water/displacement.cginc" 80 | #include "hlsl/water/meansky.cginc" 81 | #include "hlsl/water/radiance.cginc" 82 | #include "hlsl/water/depth.cginc" 83 | #include "hlsl/water/foam.cginc" 84 | #pragma multi_compile_fog 85 | #pragma shader_feature USE_DISPLACEMENT 86 | #pragma shader_feature USE_MEAN_SKY_RADIANCE 87 | #pragma shader_feature USE_FILTERING 88 | #pragma shader_feature USE_FOAM 89 | #pragma shader_feature BLINN_PHONG 90 | #pragma exclude_renderers d3d11_9x 91 | #pragma target 3.0 92 | 93 | uniform sampler2D _CameraDepthTexture; 94 | uniform sampler2D _HeightTexture; 95 | uniform sampler2D _NormalTexture; 96 | uniform sampler2D _FoamTexture; 97 | uniform sampler2D _ShoreTexture; 98 | uniform sampler2D _ReflectionTexture; uniform float4 _ReflectionTexture_TexelSize; 99 | uniform samplerCUBE _SkyTexture; 100 | uniform sampler2D _RefractionTexture; 101 | 102 | uniform float4x4 _ViewProjectInverse; 103 | 104 | uniform float4 _TimeEditor; 105 | uniform float _AmbientDensity; 106 | uniform float _DiffuseDensity; 107 | uniform float _HeightIntensity; 108 | uniform float _NormalIntensity; 109 | uniform float _TextureTiling; 110 | 111 | uniform float4 _LightColor0; 112 | uniform float3 _SurfaceColor; 113 | uniform float3 _ShoreColor; 114 | uniform float3 _DepthColor; 115 | // Wind direction in world coordinates, amplitude encoded as the length of the vector 116 | uniform float2 _WindDirection; 117 | uniform float _WaveTiling; 118 | uniform float _WaveSteepness; 119 | uniform float _WaveAmplitudeFactor; 120 | // Displacement amplitude of multiple waves, x = smallest waves, w = largest waves 121 | uniform float4 _WaveAmplitude; 122 | // Intensity of multiple waves, affects the frequency of specific waves, x = smallest waves, w = largest waves 123 | uniform float4 _WavesIntensity; 124 | // Noise of multiple waves, x = smallest waves, w = largest waves 125 | uniform float4 _WavesNoise; 126 | // Affects how fast the colors will fade out, thus, use smaller values (eg. 0.05f). 127 | // to have crystal clear water and bigger to achieve "muddy" water. 128 | uniform float _WaterClarity; 129 | // Water transparency along eye vector 130 | uniform float _WaterTransparency; 131 | // Horizontal extinction of the RGB channels, in world coordinates. 132 | // Red wavelengths dissapear(get absorbed) at around 5m, followed by green(75m) and blue(300m). 133 | uniform float3 _HorizontalExtinction; 134 | uniform float _Shininess; 135 | // xy = Specular intensity values, z = shininess exponential factor. 136 | uniform float3 _SpecularValues; 137 | // x = index of refraction constant, y = refraction intensity 138 | // if you want to empasize reflections use values smaller than 0 for refraction intensity. 139 | uniform float2 _RefractionValues; 140 | // Amount of wave refraction, of zero then no refraction. 141 | uniform float _RefractionScale; 142 | // Reflective radiance factor. 143 | uniform float _RadianceFactor; 144 | // Reflection distortion, the higher the more distortion. 145 | uniform float _Distortion; 146 | // x = range for shore foam, y = range for near shore foam, z = threshold for wave foam 147 | uniform float3 _FoamRanges; 148 | // x = noise for shore, y = noise for outer 149 | // z = speed of the noise for shore, y = speed of the noise for outer, not that speed can be negative 150 | uniform float4 _FoamNoise; 151 | uniform float2 _FoamTiling; 152 | // Extra speed applied to the wind speed near the shore 153 | uniform float _FoamSpeed; 154 | uniform float _FoamIntensity; 155 | uniform float _ShoreFade; 156 | 157 | struct VertexInput { 158 | float4 vertex : POSITION; 159 | }; 160 | struct VertexOutput { 161 | float4 pos : SV_POSITION; 162 | float2 uv : TEXCOORD0; 163 | float3 normal : TEXCOORD1; // world normal 164 | float3 tangent : TEXCOORD2; 165 | float3 bitangent : TEXCOORD3; 166 | float3 worldPos : TEXCOORD4; 167 | float4 projPos : TEXCOORD5; 168 | float timer : TEXCOORD6; 169 | float4 wind : TEXCOORD7; // xy = normalized wind, zw = wind multiplied with timer 170 | UNITY_FOG_COORDS(8) 171 | }; 172 | 173 | VertexOutput vert(VertexInput v) 174 | { 175 | VertexOutput o = (VertexOutput)0; 176 | 177 | float2 windDir = _WindDirection; 178 | float windSpeed = length(_WindDirection); 179 | windDir /= windSpeed; 180 | float timer = (_Time + _TimeEditor) * windSpeed * 10; 181 | 182 | float4 modelPos = v.vertex; 183 | float3 worldPos = mul(unity_ObjectToWorld, float4(modelPos.xyz, 1)); 184 | half3 normal = half3(0, 1, 0); 185 | 186 | #ifdef USE_DISPLACEMENT 187 | float cameraDistance = length(_WorldSpaceCameraPos.xyz - worldPos); 188 | float2 noise = GetNoise(worldPos.xz, timer * windDir * 0.5); 189 | 190 | half3 tangent; 191 | float4 waveSettings = float4(windDir, _WaveSteepness, _WaveTiling); 192 | float4 waveAmplitudes = _WaveAmplitude * _WaveAmplitudeFactor; 193 | worldPos = ComputeDisplacement(worldPos, cameraDistance, noise, timer, 194 | waveSettings, waveAmplitudes, _WavesIntensity, _WavesNoise, 195 | normal, tangent); 196 | 197 | // add extra noise height from a heightmap 198 | float heightIntensity = _HeightIntensity * (1.0 - cameraDistance / 100.0) * _WaveAmplitude; 199 | float2 texCoord = worldPos.xz * 0.05 *_TextureTiling; 200 | if (heightIntensity > 0.02) 201 | { 202 | float height = ComputeNoiseHeight(_HeightTexture, _WavesIntensity, _WavesNoise, 203 | texCoord, noise, timer); 204 | worldPos.y += height * heightIntensity; 205 | } 206 | 207 | modelPos = mul(unity_WorldToObject, float4(worldPos, 1)); 208 | o.tangent = tangent; 209 | o.bitangent = cross(normal, tangent); 210 | #endif 211 | float2 uv = worldPos.xz; 212 | 213 | o.timer = timer; 214 | o.wind.xy = windDir; 215 | o.wind.zw = windDir * timer; 216 | 217 | o.uv = uv * 0.05 * _TextureTiling; 218 | o.pos = UnityObjectToClipPos(modelPos); 219 | o.worldPos = worldPos; 220 | o.projPos = ComputeScreenPos(o.pos); 221 | o.normal = normal; 222 | 223 | UNITY_TRANSFER_FOG(o, o.pos); 224 | 225 | return o; 226 | } 227 | 228 | float4 frag(VertexOutput fs_in, float facing : VFACE) : COLOR 229 | { 230 | float timer = fs_in.timer; 231 | float2 windDir = fs_in.wind.xy; 232 | float2 timedWindDir = fs_in.wind.zw; 233 | float2 ndcPos = float2(fs_in.projPos.xy / fs_in.projPos.w); 234 | float3 eyeDir = normalize(_WorldSpaceCameraPos.xyz - fs_in.worldPos); 235 | float3 surfacePosition = fs_in.worldPos; 236 | half3 lightColor = _LightColor0.rgb; 237 | 238 | //wave normal 239 | #ifdef USE_DISPLACEMENT 240 | half3 normal = ComputeNormal(_NormalTexture, surfacePosition.xz, fs_in.uv, 241 | fs_in.normal, fs_in.tangent, fs_in.bitangent, _WavesNoise, _WavesIntensity, timedWindDir); 242 | #else 243 | half3 normal = ComputeNormal(_NormalTexture, surfacePosition.xz, fs_in.uv, 244 | fs_in.normal, 0, 0, _WavesNoise, _WavesIntensity, timedWindDir); 245 | #endif 246 | normal = normalize(lerp(fs_in.normal, normalize(normal), _NormalIntensity)); 247 | 248 | // compute refracted color 249 | float depth = tex2Dproj(_CameraDepthTexture, UNITY_PROJ_COORD(fs_in.projPos.xyww)); 250 | float3 depthPosition = NdcToWorldPos(_ViewProjectInverse, float3(ndcPos, depth)); 251 | float waterDepth = surfacePosition.y - depthPosition.y; // horizontal water depth 252 | float viewWaterDepth = length(surfacePosition - depthPosition); // water depth from the view direction(water accumulation) 253 | float2 dudv = ndcPos; 254 | { 255 | // refraction based on water depth 256 | float refractionScale = _RefractionScale * min(waterDepth, 1.0f); 257 | float2 delta = float2(sin(timer + 3.0f * abs(depthPosition.y)), 258 | sin(timer + 5.0f * abs(depthPosition.y))); 259 | dudv += windDir * delta * refractionScale; 260 | } 261 | half3 pureRefractionColor = tex2D(_RefractionTexture, dudv).rgb; 262 | { 263 | // reverse existing applied fog for correct shore color 264 | INVERSE_FOG_COLOR(fs_in.fogCoord, pureRefractionColor); 265 | } 266 | float2 waterTransparency = float2(_WaterClarity, _WaterTransparency); 267 | float2 waterDepthValues = float2(waterDepth, viewWaterDepth); 268 | float shoreRange = max(_FoamRanges.x, _FoamRanges.y) * 2.0; 269 | half3 refractionColor = DepthRefraction(waterTransparency, waterDepthValues, shoreRange, _HorizontalExtinction, 270 | pureRefractionColor, _ShoreColor, _SurfaceColor, _DepthColor); 271 | 272 | // compute ligths's reflected radiance 273 | float3 lightDir = normalize(_WorldSpaceLightPos0); 274 | half fresnel = FresnelValue(_RefractionValues, normal, eyeDir); 275 | half3 specularColor = ReflectedRadiance(_Shininess, _SpecularValues, lightColor, lightDir, eyeDir, normal, fresnel); 276 | 277 | // compute sky's reflected radiance 278 | #ifdef USE_MEAN_SKY_RADIANCE 279 | half3 reflectColor = fresnel * MeanSkyRadiance(_SkyTexture, eyeDir, normal) * _RadianceFactor; 280 | #else 281 | half3 reflectColor = 0; 282 | #endif // #ifndef USE_MEAN_SKY_RADIANCE 283 | 284 | // compute reflected color 285 | dudv = ndcPos + _Distortion * normal.xz; 286 | #ifdef USE_FILTERING 287 | reflectColor += tex2DBicubic(_ReflectionTexture, _ReflectionTexture_TexelSize.z, dudv).rgb; 288 | #else 289 | reflectColor += tex2D(_ReflectionTexture, dudv).rgb; 290 | #endif // #ifdef USE_FILTERING 291 | 292 | // shore foam 293 | #ifdef USE_FOAM 294 | float maxAmplitude = max(max(_WaveAmplitude.x, _WaveAmplitude.y), _WaveAmplitude.z); 295 | half foam = FoamValue(_ShoreTexture, _FoamTexture, _FoamTiling, 296 | _FoamNoise, _FoamSpeed * windDir, _FoamRanges, maxAmplitude, 297 | surfacePosition, depthPosition, eyeDir, waterDepth, timedWindDir, timer); 298 | foam *= _FoamIntensity; 299 | #else 300 | half foam = 0; 301 | #endif // #ifdef USE_FOAM 302 | 303 | half shoreFade = saturate(waterDepth * _ShoreFade); 304 | // ambient + diffuse 305 | half3 ambientColor = UNITY_LIGHTMODEL_AMBIENT.rgb * _AmbientDensity + saturate(dot(normal, lightDir)) * _DiffuseDensity; 306 | // refraction color with depth based color 307 | pureRefractionColor = lerp(pureRefractionColor, reflectColor, fresnel * saturate(waterDepth / (_FoamRanges.x * 0.4))); 308 | pureRefractionColor = lerp(pureRefractionColor, _ShoreColor, 0.30 * shoreFade); 309 | // compute final color 310 | half3 color = lerp(refractionColor, reflectColor, fresnel); 311 | color = saturate(ambientColor + color + max(specularColor, foam * lightColor)); 312 | color = lerp(pureRefractionColor + specularColor * shoreFade, color, shoreFade); 313 | UNITY_APPLY_FOG(fs_in.fogCoord, color); 314 | 315 | #ifdef DEBUG_NORMALS 316 | color.rgb = 0.5 + 2 * ambientColor + specularColor + clamp(dot(normal, lightDir), 0, 1) * 0.5; 317 | #endif 318 | 319 | return float4(color, 1.0); 320 | } 321 | ENDCG 322 | } 323 | } 324 | CustomEditor "WaterShaderGUI" 325 | FallBack "Diffuse" 326 | } 327 | -------------------------------------------------------------------------------- /textures/foam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/textures/foam.png -------------------------------------------------------------------------------- /textures/foam_shore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/textures/foam_shore.png -------------------------------------------------------------------------------- /textures/skybox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/textures/skybox.png -------------------------------------------------------------------------------- /textures/water_height.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/textures/water_height.png -------------------------------------------------------------------------------- /textures/water_normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tuxalin/water-shader/5370b57f2384cdac460c9f704e6380e71b778359/textures/water_normal.png --------------------------------------------------------------------------------