Added seascape shader example.

This commit is contained in:
Chillance 2018-10-07 15:59:47 +02:00
parent b56a517782
commit 4291ffb4f0
7 changed files with 729 additions and 0 deletions

View File

@ -0,0 +1,118 @@
# Shadertoy to Pixel shader conversion
This will show you an example how you can take an example from shadertoy.com and use it with the Pixel Shader support.
"Seascape" by Alexander Alekseev aka TDM - 2014 is a nice one. It's very impressive and doesn't use textures so that simplifies things for us. Let's try that one.
Looking at the seascape.glsl example you can see **iResolution**, **iTime** and **iMouse** in there.
These are commonly needed to be exposed because these things are coming from outside and needs to be updated. Any other variable you need to have changed/updated from code can be exposed like those.
## Exposing variables
How to expose variables like this?
Well, first we need to figure out what type of variables they are. Looking at the shader, you can see **iResolution.x** in there. This tells you that it's not a simple type. In this case it's a **vec2***, a **Vector containing 2 values, x and y**. This makes sense since resolution is described by x and y. That is, width and height.
And thus, we create our variable in Go with like so:
```
uResolution := mgl32.Vec2{float32(win.Bounds().W()), float32(win.Bounds().H())}
```
That is, to be of the type **mgl32.Vec2**. Here we create it by taking the window's width and height.
**iTime** is just a float, and thus that is just created like so:
```
var uTime float32
```
For the **iMouse**, it's a **mgl32.Vec4**, a **Vector containing 4 variables**. We only use x and y for the mouse position here though.
```
var uMouse mgl32.Vec4
```
And finally, to make our variables available in the shader itself we use:
```
canvas.SetUniform(Name string, Value interface{})
```
We create a handy function to do this:
``` go
func EasyBindUniforms(c *pixelgl.Canvas, unifs ...interface{}) {
if len(unifs)%2 != 0 {
panic("needs to be divisable by 2")
}
for i := 0; i < len(unifs); i += 2 {
c.SetUniform(unifs[i+0].(string), unifs[i+1])
}
}
```
and we call that function like so:
``` go
EasyBindUniforms(canvas,
"u_resolution", &uResolution,
"u_time", &uTime,
"u_mouse", &uMouse,
)
```
## Updating shader source file
We also need to do some updates to the shader file itself to match these variables. First thing would be to add the variables we exposed in Go.
```
uniform vec2 u_resolution;
uniform float u_time;
uniform vec4 u_mouse;
```
Then we just rename the variables to match.
We also need to rename the main function itself, as the one used here is specific for use with shadertoy. For our shader, the entrypoint is main(). So we rename:
```
void mainImage( out vec4 fragColor, in vec2 fragCoord )
```
to
```
void main() {
```
Also, rename:
```
fragCoord
```
to
```
gl_FragCoord
```
and rename:
```
fragColor
```
to
```
gl_FragColor
```
because these are available globaly in the OpenGL space for the shader.
## Using shader
To use the shader in our canvas we do:
```
canvas.SetFragmentShader(fragSource string)
```
where fragSource is the fragment shader, not a path fo a file.
#
## Result converting shadertoy shader to use with Pixel
Here is a diff of the changes:
![seascape animation](shader_diffs.png "Seascape animation")
And that is it. Running the program we should see this:
![seascape animation](seascape.gif "Seascape animation")

View File

@ -0,0 +1,54 @@
package main
import (
"io/ioutil"
"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
)
// Pixel Shader utility functions
// EasyBindUniforms does all the work for you, just pass in a
// valid array adhering to format: String, Variable, ...
//
// example:
//
// var uTimeVar float32
// var uMouseVar mgl32.Vec4
//
// EasyBindUniforms(win.GetCanvas(),
// "u_time", &uTimeVar,
// "u_mouse", &uMouseVar,
// )
//
func EasyBindUniforms(c *pixelgl.Canvas, unifs ...interface{}) {
if len(unifs)%2 != 0 {
panic("needs to be divisable by 2")
}
for i := 0; i < len(unifs); i += 2 {
c.SetUniform(unifs[i+0].(string), unifs[i+1])
}
}
// CenterWindow will... center the window
func CenterWindow(win *pixelgl.Window) {
x, y := pixelgl.PrimaryMonitor().Size()
width, height := win.Bounds().Size().XY()
win.SetPos(
pixel.V(
x/2-width/2,
y/2-height/2,
),
)
}
// LoadFileToString loads the contents of a file into a string
func LoadFileToString(filename string) (string, error) {
b, err := ioutil.ReadFile(filename)
if err != nil {
return "", err
}
return string(b), nil
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

View File

@ -0,0 +1,184 @@
package main
import (
"time"
"github.com/go-gl/mathgl/mgl32"
"github.com/faiface/pixel"
"github.com/faiface/pixel/pixelgl"
"golang.org/x/image/colornames"
)
func run() {
// Set up window configs
cfg := pixelgl.WindowConfig{ // Default: 1024 x 768
Title: "Golang Seascape from Shadertoy",
Bounds: pixel.R(0, 0, 1024, 768),
VSync: true,
}
win, err := pixelgl.NewWindow(cfg)
if err != nil {
panic(err)
}
camVector := win.Bounds().Center()
bounds := win.Bounds()
bounds.Max = bounds.Max.ScaledXY(pixel.V(1.0, 1.0))
// I am putting all shader example initializing stuff here for
// easier reference to those learning to use this functionality
fragSource, err := LoadFileToString("shaders/seascape.glsl")
if err != nil {
panic(err)
}
var uMouse mgl32.Vec4
var uTime float32
canvas := win.Canvas()
uResolution := mgl32.Vec2{float32(win.Bounds().W()), float32(win.Bounds().H())}
EasyBindUniforms(canvas,
"u_resolution", &uResolution,
"u_time", &uTime,
"u_mouse", &uMouse,
)
canvas.SetFragmentShader(fragSource)
start := time.Now()
// Game Loop
for !win.Closed() {
uTime = float32(time.Since(start).Seconds())
mpos := win.MousePosition()
uMouse[0] = float32(mpos.X)
uMouse[1] = float32(mpos.Y)
win.Clear(colornames.Black)
// Drawing to the screen
canvas.Draw(win, pixel.IM.Moved(camVector))
win.Update()
}
}
func main() {
pixelgl.Run(run)
}
var cloudsFragmentShader = `
#version 330 core
#ifdef GL_ES
precision highp float;
#endif
#define HOW_CLOUDY 0.2
#define SHADOW_THRESHOLD 0.4
#define SHADOW 0.3
#define SUBSURFACE 1.0
#define WIND_DIRECTION 0.3
#define TIME_SCALE 0.6
#define SCALE 0.1
//#define ENABLE_SHAFTS
in vec2 texcoords;
out vec4 fragColor;
mat2 RM = mat2(cos(WIND_DIRECTION), -sin(WIND_DIRECTION), sin(WIND_DIRECTION), cos(WIND_DIRECTION));
uniform float u_time;
uniform vec2 u_mouse;
//uniform vec2 u_resolution;
uniform vec4 u_texbounds;
uniform sampler2D u_texture;
uniform vec2 u_gopherpos;
float hash( float n )
{
return fract(sin(n)*758.5453);
}
float noise( in vec3 x )
{
vec3 p = floor(x);
vec3 f = fract(x);
float n = p.x + p.y*57.0 + p.z*800.0;
float res = mix(mix(mix( hash(n+ 0.0), hash(n+ 1.0),f.x), mix( hash(n+ 57.0), hash(n+ 58.0),f.x),f.y),
mix(mix( hash(n+800.0), hash(n+801.0),f.x), mix( hash(n+857.0), hash(n+858.0),f.x),f.y),f.z);
return res;
}
float fbm( vec3 p )
{
float f = 0.0;
f += 0.50000*noise( p ); p = p*2.02;
f -= 0.25000*noise( p ); p = p*2.03;
f += 0.12500*noise( p ); p = p*3.01;
f += 0.06250*noise( p ); p = p*3.04;
f += 0.03500*noise( p ); p = p*4.01;
f += 0.01250*noise( p ); p = p*4.04;
f -= 0.00125*noise( p );
return f/0.784375;
}
float cloud(vec3 p)
{
p-=fbm(vec3(p.x,p.y,0.0)*0.5)*1.25;
float a = min((fbm(p*3.0)*2.2-1.1), 0.0);
return a*a;
}
float shadow = 1.0;
float clouds(vec2 p){
float ic = cloud(vec3(p * 2.0, u_time*0.01 * TIME_SCALE)) / HOW_CLOUDY;
float init = smoothstep(0.1, 1.0, ic) * 5.0;
shadow = smoothstep(0.0, SHADOW_THRESHOLD, ic) * SHADOW + (1.0 - SHADOW);
init = (init * cloud(vec3(p * (6.0), u_time*0.01 * TIME_SCALE)) * ic);
init = (init * (cloud(vec3(p * (11.0), u_time*0.01 * TIME_SCALE))*0.5 + 0.4) * init);
return min(1.0, init);
}
//uniform sampler2D bb;
float cloudslowres(vec2 p){
return 1.0 - (texture(u_texture, p).a - 0.9) * 10.0;
}
vec2 ratio = vec2(1.0, 1.0);
vec4 getresult(){
vec2 uvmouse = (u_mouse/(texcoords - u_texbounds.xy));
vec2 t = (texcoords - u_texbounds.xy) / u_texbounds.zw;
//vec2 surfacePosition = ((( t ) * vec2(u_gopherpos.x , u_gopherpos.y)) * 2.0 - 1.0)*SCALE;
vec2 surfacePosition = t+u_gopherpos*10.0;
vec2 position = ( surfacePosition * SCALE);
vec2 sun = (uvmouse.xy * vec2(texcoords.x / texcoords.y, 1.0)*2.0-1.0) * SCALE;
float dst = distance(sun * ratio, position * ratio);
float suni = pow(dst + 1.0, -10.0);
float shaft =0.0;
float st = 0.05;
float w = 1.0;
vec2 dir = sun - position;
float c = clouds(position);
#ifdef ENABLE_SHAFTS
for(int i=0;i<50;i++){
float occl = cloudslowres(clamp((t) + dir * st, 0.0, 1.0));
w *= 0.99;
st *= 1.05;
shaft += max(0.0, (1.0 - occl)) * w;
}
#endif
shadow = min(1.0, shadow + suni * suni * 0.2 * SUBSURFACE);
suni *= (shaft * 0.03);
return vec4(pow(mix(vec3(shadow), pow(vec3(0.23, 0.33, 0.48), vec3(2.2)) + suni, c), vec3(1.0/2.2)), c*0.1 + 0.9);
}
void main( void ) {
fragColor = getresult().rgba;
}
`

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

View File

@ -0,0 +1,189 @@
/*
* "Seascape" by Alexander Alekseev aka TDM - 2014
* License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
* Contact: tdmaav@gmail.com
*/
uniform vec2 u_resolution;
uniform float u_time;
uniform vec4 u_mouse;
const int NUM_STEPS = 8;
const float PI = 3.141592;
const float EPSILON = 1e-3;
#define EPSILON_NRM (0.1 / u_resolution.x)
// sea
const int ITER_GEOMETRY = 3;
const int ITER_FRAGMENT = 5;
const float SEA_HEIGHT = 0.6;
const float SEA_CHOPPY = 4.0;
const float SEA_SPEED = 0.8;
const float SEA_FREQ = 0.16;
const vec3 SEA_BASE = vec3(0.1,0.19,0.22);
const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);
#define SEA_TIME (1.0 + u_time * SEA_SPEED)
const mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);
// math
mat3 fromEuler(vec3 ang) {
vec2 a1 = vec2(sin(ang.x),cos(ang.x));
vec2 a2 = vec2(sin(ang.y),cos(ang.y));
vec2 a3 = vec2(sin(ang.z),cos(ang.z));
mat3 m;
m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);
m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);
return m;
}
float hash( vec2 p ) {
float h = dot(p,vec2(127.1,311.7));
return fract(sin(h)*43758.5453123);
}
float noise( in vec2 p ) {
vec2 i = floor( p );
vec2 f = fract( p );
vec2 u = f*f*(3.0-2.0*f);
return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ),
hash( i + vec2(1.0,0.0) ), u.x),
mix( hash( i + vec2(0.0,1.0) ),
hash( i + vec2(1.0,1.0) ), u.x), u.y);
}
// lighting
float diffuse(vec3 n,vec3 l,float p) {
return pow(dot(n,l) * 0.4 + 0.6,p);
}
float specular(vec3 n,vec3 l,vec3 e,float s) {
float nrm = (s + 8.0) / (PI * 8.0);
return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
}
// sky
vec3 getSkyColor(vec3 e) {
e.y = max(e.y,0.0);
return vec3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4);
}
// sea
float sea_octave(vec2 uv, float choppy) {
uv += noise(uv);
vec2 wv = 1.0-abs(sin(uv));
vec2 swv = abs(cos(uv));
wv = mix(wv,swv,wv);
return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
}
float map(vec3 p) {
float freq = SEA_FREQ;
float amp = SEA_HEIGHT;
float choppy = SEA_CHOPPY;
vec2 uv = p.xz; uv.x *= 0.75;
float d, h = 0.0;
for(int i = 0; i < ITER_GEOMETRY; i++) {
d = sea_octave((uv+SEA_TIME)*freq,choppy);
d += sea_octave((uv-SEA_TIME)*freq,choppy);
h += d * amp;
uv *= octave_m; freq *= 1.9; amp *= 0.22;
choppy = mix(choppy,1.0,0.2);
}
return p.y - h;
}
float map_detailed(vec3 p) {
float freq = SEA_FREQ;
float amp = SEA_HEIGHT;
float choppy = SEA_CHOPPY;
vec2 uv = p.xz; uv.x *= 0.75;
float d, h = 0.0;
for(int i = 0; i < ITER_FRAGMENT; i++) {
d = sea_octave((uv+SEA_TIME)*freq,choppy);
d += sea_octave((uv-SEA_TIME)*freq,choppy);
h += d * amp;
uv *= octave_m; freq *= 1.9; amp *= 0.22;
choppy = mix(choppy,1.0,0.2);
}
return p.y - h;
}
vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {
float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0);
fresnel = pow(fresnel,3.0) * 0.65;
vec3 reflected = getSkyColor(reflect(eye,n));
vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;
vec3 color = mix(refracted,reflected,fresnel);
float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);
color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
color += vec3(specular(n,l,eye,60.0));
return color;
}
// tracing
vec3 getNormal(vec3 p, float eps) {
vec3 n;
n.y = map_detailed(p);
n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;
n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;
n.y = eps;
return normalize(n);
}
float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {
float tm = 0.0;
float tx = 1000.0;
float hx = map(ori + dir * tx);
if(hx > 0.0) return tx;
float hm = map(ori + dir * tm);
float tmid = 0.0;
for(int i = 0; i < NUM_STEPS; i++) {
tmid = mix(tm,tx, hm/(hm-hx));
p = ori + dir * tmid;
float hmid = map(p);
if(hmid < 0.0) {
tx = tmid;
hx = hmid;
} else {
tm = tmid;
hm = hmid;
}
}
return tmid;
}
// main
void main()
{
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
uv = uv * 2.0 - 1.0;
uv.x *= u_resolution.x / u_resolution.y;
float time = u_time * 0.3 + u_mouse.x*0.01;
// ray
vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time);
vec3 ori = vec3(0.0,3.5,time*5.0);
vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.15;
dir = normalize(dir) * fromEuler(ang);
// tracing
vec3 p;
heightMapTracing(ori,dir,p);
vec3 dist = p - ori;
vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);
vec3 light = normalize(vec3(0.0,1.0,0.8));
// color
vec3 color = mix(
getSkyColor(dir),
getSeaColor(p,n,light,dir,dist),
pow(smoothstep(0.0,-0.05,dir.y),0.3));
// post
gl_FragColor = vec4(pow(color,vec3(0.75)), 1.0);
}

View File

@ -0,0 +1,184 @@
/*
* "Seascape" by Alexander Alekseev aka TDM - 2014
* License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
* Contact: tdmaav@gmail.com
*/
const int NUM_STEPS = 8;
const float PI = 3.141592;
const float EPSILON = 1e-3;
#define EPSILON_NRM (0.1 / iResolution.x)
// sea
const int ITER_GEOMETRY = 3;
const int ITER_FRAGMENT = 5;
const float SEA_HEIGHT = 0.6;
const float SEA_CHOPPY = 4.0;
const float SEA_SPEED = 0.8;
const float SEA_FREQ = 0.16;
const vec3 SEA_BASE = vec3(0.1,0.19,0.22);
const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);
#define SEA_TIME (1.0 + iTime * SEA_SPEED)
const mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);
// math
mat3 fromEuler(vec3 ang) {
vec2 a1 = vec2(sin(ang.x),cos(ang.x));
vec2 a2 = vec2(sin(ang.y),cos(ang.y));
vec2 a3 = vec2(sin(ang.z),cos(ang.z));
mat3 m;
m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);
m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);
m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);
return m;
}
float hash( vec2 p ) {
float h = dot(p,vec2(127.1,311.7));
return fract(sin(h)*43758.5453123);
}
float noise( in vec2 p ) {
vec2 i = floor( p );
vec2 f = fract( p );
vec2 u = f*f*(3.0-2.0*f);
return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ),
hash( i + vec2(1.0,0.0) ), u.x),
mix( hash( i + vec2(0.0,1.0) ),
hash( i + vec2(1.0,1.0) ), u.x), u.y);
}
// lighting
float diffuse(vec3 n,vec3 l,float p) {
return pow(dot(n,l) * 0.4 + 0.6,p);
}
float specular(vec3 n,vec3 l,vec3 e,float s) {
float nrm = (s + 8.0) / (PI * 8.0);
return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;
}
// sky
vec3 getSkyColor(vec3 e) {
e.y = max(e.y,0.0);
return vec3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4);
}
// sea
float sea_octave(vec2 uv, float choppy) {
uv += noise(uv);
vec2 wv = 1.0-abs(sin(uv));
vec2 swv = abs(cos(uv));
wv = mix(wv,swv,wv);
return pow(1.0-pow(wv.x * wv.y,0.65),choppy);
}
float map(vec3 p) {
float freq = SEA_FREQ;
float amp = SEA_HEIGHT;
float choppy = SEA_CHOPPY;
vec2 uv = p.xz; uv.x *= 0.75;
float d, h = 0.0;
for(int i = 0; i < ITER_GEOMETRY; i++) {
d = sea_octave((uv+SEA_TIME)*freq,choppy);
d += sea_octave((uv-SEA_TIME)*freq,choppy);
h += d * amp;
uv *= octave_m; freq *= 1.9; amp *= 0.22;
choppy = mix(choppy,1.0,0.2);
}
return p.y - h;
}
float map_detailed(vec3 p) {
float freq = SEA_FREQ;
float amp = SEA_HEIGHT;
float choppy = SEA_CHOPPY;
vec2 uv = p.xz; uv.x *= 0.75;
float d, h = 0.0;
for(int i = 0; i < ITER_FRAGMENT; i++) {
d = sea_octave((uv+SEA_TIME)*freq,choppy);
d += sea_octave((uv-SEA_TIME)*freq,choppy);
h += d * amp;
uv *= octave_m; freq *= 1.9; amp *= 0.22;
choppy = mix(choppy,1.0,0.2);
}
return p.y - h;
}
vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {
float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0);
fresnel = pow(fresnel,3.0) * 0.65;
vec3 reflected = getSkyColor(reflect(eye,n));
vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;
vec3 color = mix(refracted,reflected,fresnel);
float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);
color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;
color += vec3(specular(n,l,eye,60.0));
return color;
}
// tracing
vec3 getNormal(vec3 p, float eps) {
vec3 n;
n.y = map_detailed(p);
n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;
n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;
n.y = eps;
return normalize(n);
}
float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {
float tm = 0.0;
float tx = 1000.0;
float hx = map(ori + dir * tx);
if(hx > 0.0) return tx;
float hm = map(ori + dir * tm);
float tmid = 0.0;
for(int i = 0; i < NUM_STEPS; i++) {
tmid = mix(tm,tx, hm/(hm-hx));
p = ori + dir * tmid;
float hmid = map(p);
if(hmid < 0.0) {
tx = tmid;
hx = hmid;
} else {
tm = tmid;
hm = hmid;
}
}
return tmid;
}
// main
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
vec2 uv = fragCoord.xy / iResolution.xy;
uv = uv * 2.0 - 1.0;
uv.x *= iResolution.x / iResolution.y;
float time = iTime * 0.3 + iMouse.x*0.01;
// ray
vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time);
vec3 ori = vec3(0.0,3.5,time*5.0);
vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.15;
dir = normalize(dir) * fromEuler(ang);
// tracing
vec3 p;
heightMapTracing(ori,dir,p);
vec3 dist = p - ori;
vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);
vec3 light = normalize(vec3(0.0,1.0,0.8));
// color
vec3 color = mix(
getSkyColor(dir),
getSeaColor(p,n,light,dir,dist),
pow(smoothstep(0.0,-0.05,dir.y),0.3));
// post
fragColor = vec4(pow(color,vec3(0.75)), 1.0);
}