Created
February 14, 2024 00:10
-
-
Save rngtm/c91cfd99a70062ee5732b6078b4488ba to your computer and use it in GitHub Desktop.
チョコレートの海シェーダー (GLSL)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Original : https://www.shadertoy.com/view/MdXyzX | |
// afl_ext 2017-2023 | |
// Now with 2023 refresh | |
// MIT License | |
// Use your mouse to move the camera around! Press the Left Mouse Button on the image to look around! | |
#define DRAG_MULT 0.28 // changes how much waves pull on the water | |
#define WATER_DEPTH 1.0 // how deep is the water | |
#define CAMERA_HEIGHT 1.5 // how high the camera should be | |
#define ITERATIONS_RAYMARCH 12 // waves iterations of raymarching | |
#define ITERATIONS_NORMAL 40 // waves iterations when calculating normals | |
#define SKY_COLOR hsv2rgb(vec3(14.0 / 256.0, 210.0 / 256.0, 6.0)) | |
#define FOG_COLOR hsv2rgb(vec3(16.0 / 256.0, 220.0 / 256.0, 6.0)) | |
#define FOG_START 5.0 | |
#define FOG_END 200.0 | |
#define FOG_EXPONENT 1.2 | |
#define POST_BRIGHTNESS_SKY 2.0 | |
#define POST_BRIGHTNESS 2.0 | |
#define CHOCOLATE_BRIGHTNESS 0.2 | |
#define BASE_COLOR vec3(0.16f, 0.11f, 0.11f) | |
#define NormalizedMouse (iMouse.xy / iResolution.xy) // normalize mouse coords | |
// Calculates wave value and its derivative, | |
// for the wave direction, position in space, wave frequency and time | |
vec2 wavedx(vec2 position, vec2 direction, float frequency, float timeshift) { | |
float x = dot(direction, position) * frequency + timeshift; | |
float wave = exp(sin(x) - 1.0); | |
float dx = wave * cos(x); | |
return vec2(wave, -dx); | |
} | |
// Calculates waves by summing octaves of various waves with various parameters | |
float getwaves(vec2 position, int iterations) { | |
float iter = 0.0; // this will help generating well distributed wave directions | |
float frequency = 1.0; // frequency of the wave, this will change every iteration | |
// float timeMultiplier = 1.0; // time multiplier for the wave, this will change every iteration | |
float timeMultiplier = 1.0; // time multiplier for the wave, this will change every iteration | |
float weight = 1.0;// weight in final sum for the wave, this will change every iteration | |
float sumOfValues = 0.0; // will store final sum of values | |
float sumOfWeights = 0.0; // will store final sum of weights | |
for(int i=0; i < iterations; i++) { | |
// generate some wave direction that looks kind of random | |
vec2 p = vec2(sin(iter), cos(iter)); | |
// calculate wave data | |
vec2 res = wavedx(position, p, frequency, iTime * timeMultiplier); | |
// shift position around according to wave drag and derivative of the wave | |
position += p * res.y * weight * DRAG_MULT; | |
// add the results to sums | |
sumOfValues += res.x * weight; | |
sumOfWeights += weight; | |
// modify next octave parameters | |
weight *= 0.82; | |
frequency *= 1.18; | |
timeMultiplier *= 1.07; | |
// add some kind of random value to make next wave look random too | |
iter += 1232.399963; | |
} | |
// calculate and return | |
return sumOfValues / sumOfWeights; | |
} | |
// Raymarches the ray from top water layer boundary to low water layer boundary | |
float raymarchwater(vec3 camera, vec3 start, vec3 end, float depth) { | |
vec3 pos = start; | |
vec3 dir = normalize(end - start); | |
for(int i=0; i < 64; i++) { | |
// the height is from 0 to -depth | |
float height = getwaves(pos.xz, ITERATIONS_RAYMARCH) * depth - depth; | |
// if the waves height almost nearly matches the ray height, assume its a hit and return the hit distance | |
if(height + 0.01 > pos.y) { | |
return distance(pos, camera); | |
} | |
// iterate forwards according to the height mismatch | |
pos += dir * (pos.y - height); | |
} | |
// if hit was not registered, just assume hit the top layer, | |
// this makes the raymarching faster and looks better at higher distances | |
return distance(start, camera); | |
} | |
// Calculate normal at point by calculating the height at the pos and 2 additional points very close to pos | |
vec3 normal(vec2 pos, float e, float depth) { | |
vec2 ex = vec2(e, 0); | |
float H = getwaves(pos.xy, ITERATIONS_NORMAL) * depth; | |
vec3 a = vec3(pos.x, H, pos.y); | |
return normalize( | |
cross( | |
a - vec3(pos.x - e, getwaves(pos.xy - ex.xy, ITERATIONS_NORMAL) * depth, pos.y), | |
a - vec3(pos.x, getwaves(pos.xy + ex.yx, ITERATIONS_NORMAL) * depth, pos.y + e) | |
) | |
); | |
} | |
// Helper function generating a rotation matrix around the axis by the angle | |
mat3 createRotationMatrixAxisAngle(vec3 axis, float angle) { | |
float s = sin(angle); | |
float c = cos(angle); | |
float oc = 1.0 - c; | |
return mat3( | |
oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, | |
oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, | |
oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c | |
); | |
} | |
// Helper function that generates camera ray based on UV and mouse | |
vec3 getRay(vec2 fragCoord) { | |
vec2 uv = ((fragCoord.xy / iResolution.xy) * 2.0 - 1.0) * vec2(iResolution.x / iResolution.y, 1.0); | |
// for fisheye, uncomment following line and comment the next one | |
//vec3 proj = normalize(vec3(uv.x, uv.y, 1.0) + vec3(uv.x, uv.y, -1.0) * pow(length(uv), 2.0) * 0.05); | |
vec3 proj = normalize(vec3(uv.x, uv.y, 1.5)); | |
if(iResolution.x < 600.0) { | |
return proj; | |
} | |
return createRotationMatrixAxisAngle(vec3(0.0, -1.0, 0.0), 3.0 * ((NormalizedMouse.x + 0.5) * 2.0 - 1.0)) | |
* createRotationMatrixAxisAngle(vec3(1.0, 0.0, 0.0), 0.5 + 1.5 * ((NormalizedMouse.y * 1.5) * 2.0 - 1.0)) | |
* proj; | |
} | |
// Ray-Plane intersection checker | |
float intersectPlane(vec3 origin, vec3 direction, vec3 point, vec3 normal) { | |
return clamp(dot(point - origin, normal) / dot(direction, normal), -1.0, 9991999.0); | |
} | |
vec3 hsv2rgb(vec3 c) { | |
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); | |
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); | |
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); | |
} | |
// Some very barebones but fast atmosphere approximation | |
vec3 extra_cheap_atmosphere(vec3 raydir, vec3 sundir) { | |
sundir.y = max(sundir.y, -0.07); | |
float special_trick = 1.0 / (raydir.y * 1.0 + 0.1); | |
float special_trick2 = 1.0 / (sundir.y * 11.0 + 1.0); | |
float raysundt = pow(abs(dot(sundir, raydir)), 2.0); | |
float sundt = pow(max(0.0, dot(sundir, raydir)), 8.0); | |
float mymie = sundt * special_trick * 0.2; | |
vec3 suncolor = mix(vec3(1.0), max(vec3(0.0), vec3(1.0) - SKY_COLOR / 22.4), special_trick2); | |
vec3 bluesky= SKY_COLOR / 22.4 * suncolor; | |
vec3 bluesky2 = max(vec3(0.0), bluesky - SKY_COLOR * 0.002 * (special_trick + -6.0 * sundir.y * sundir.y)); | |
bluesky2 *= special_trick * (0.24 + raysundt * 0.24); | |
return bluesky2 * (1.0 + 1.0 * pow(1.0 - raydir.y, 3.0)) + mymie * suncolor; | |
} | |
// Calculate where the sun should be, it will be moving around the sky | |
vec3 getSunDirection() { | |
return normalize(vec3(sin(iTime * 0.1), 1.0, cos(iTime * 0.1))); | |
} | |
// Get atmosphere color for given direction | |
vec3 getAtmosphere(vec3 dir) { | |
return extra_cheap_atmosphere(dir, getSunDirection()) * 0.5; | |
} | |
// Get sun color for given direction | |
float getSun(vec3 dir) { | |
return 0.0; | |
return pow(max(0.0, dot(dir, getSunDirection())), 720.0) * 210.0; | |
} | |
// Great tonemapping function from my other shader: https://www.shadertoy.com/view/XsGfWV | |
vec3 aces_tonemap(vec3 color) { | |
mat3 m1 = mat3( | |
0.59719, 0.07600, 0.02840, | |
0.35458, 0.90834, 0.13383, | |
0.04823, 0.01566, 0.83777 | |
); | |
mat3 m2 = mat3( | |
1.60475, -0.10208, -0.00327, | |
-0.53108, 1.10813, -0.07276, | |
-0.07367, -0.00605, 1.07602 | |
); | |
vec3 v = m1 * color; | |
vec3 a = v * (v + 0.0245786) - 0.000090537; | |
vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081; | |
return pow(clamp(m2 * (a / b), 0.0, 1.0), vec3(1.0 / 2.2)); | |
} | |
// Main | |
void mainImage(out vec4 fragColor, in vec2 fragCoord) { | |
// get the ray | |
vec3 ray = getRay(fragCoord); | |
if(ray.y >= 0.0) { | |
// if ray.y is positive, render the sky | |
vec3 C = getAtmosphere(ray) + getSun(ray); | |
// fragColor = vec4(aces_tonemap(C * 2.0),1.0); | |
fragColor = vec4(aces_tonemap(FOG_COLOR * POST_BRIGHTNESS_SKY) , 1.0); | |
// fragColor.rgb = SKY_COLOR; | |
return; | |
} | |
// now ray.y must be negative, water must be hit | |
// define water planes | |
vec3 waterPlaneHigh = vec3(0.0, 0.0, 0.0); | |
vec3 waterPlaneLow = vec3(0.0, -WATER_DEPTH, 0.0); | |
// define ray origin, moving around | |
vec3 origin = vec3(iTime, CAMERA_HEIGHT, iTime); | |
// calculate intersections and reconstruct positions | |
float highPlaneHit = intersectPlane(origin, ray, waterPlaneHigh, vec3(0.0, 1.0, 0.0)); | |
float lowPlaneHit = intersectPlane(origin, ray, waterPlaneLow, vec3(0.0, 1.0, 0.0)); | |
vec3 highHitPos = origin + ray * highPlaneHit; | |
vec3 lowHitPos = origin + ray * lowPlaneHit; | |
// raymatch water and reconstruct the hit pos | |
float dist = raymarchwater(origin, highHitPos, lowHitPos, WATER_DEPTH); | |
vec3 waterHitPos = origin + ray * dist; | |
// calculate normal at the hit position | |
vec3 N = normal(waterHitPos.xz, 0.01, WATER_DEPTH); | |
// smooth the normal with distance to avoid disturbing high frequency noise | |
N = mix(N, vec3(0.0, 1.0, 0.0), 0.8 * min(1.0, sqrt(dist*0.01) * 1.1)); | |
// calculate fresnel coefficient | |
float fresnel = (0.04 + (1.0-0.04)*(pow(1.0 - max(0.0, dot(-N, ray)), 5.0))); | |
// reflect the ray and make sure it bounces up | |
vec3 R = normalize(reflect(ray, N)); | |
R.y = abs(R.y); | |
// calculate the reflection and approximate subsurface scattering | |
vec3 reflection = getAtmosphere(R) + getSun(R); | |
// vec3 scattering = BASE_COLOR * 0.1 * (0.2 + (waterHitPos.y + WATER_DEPTH) / WATER_DEPTH); | |
vec3 scattering = BASE_COLOR * CHOCOLATE_BRIGHTNESS * (0.3 + (waterHitPos.y + WATER_DEPTH) / WATER_DEPTH); | |
// return the combined result | |
vec3 C = fresnel * reflection + (1.0 - fresnel) * scattering; | |
float fogDensity = saturate((dist - FOG_START) / (FOG_END - FOG_START)); | |
fogDensity = pow(fogDensity, FOG_EXPONENT); | |
C.rgb = mix(C.rgb, FOG_COLOR, fogDensity); | |
// fragColor = vec4(aces_tonemap(C * 2.0), 1.0); | |
fragColor = vec4(aces_tonemap(C * POST_BRIGHTNESS), 1.0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment