Skip to content

Instantly share code, notes, and snippets.

@jadsongmatos
Last active July 2, 2025 23:07
Show Gist options
  • Save jadsongmatos/09378f03c5599bb792f6f86a414015cf to your computer and use it in GitHub Desktop.
Save jadsongmatos/09378f03c5599bb792f6f86a414015cf to your computer and use it in GitHub Desktop.
#version 300 es
precision highp float;
out vec4 out_color;
in vec2 uv;
uniform float u_time;
uniform vec2 u_resolution;
/* Hash e ruído avançado */
vec2 hash22(vec2 p) {
return fract(sin(vec2(dot(p, vec2(127.1, 311.7)), dot(p, vec2(269.5, 183.3)))) * 43758.5453);
}
float worley(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
float minDist = 1.0;
for(int y=-1; y<=1; y++) {
for(int x=-1; x<=1; x++) {
vec2 neighbor = vec2(x,y);
vec2 point = hash22(i + neighbor);
float dist = length(neighbor + point - f);
minDist = min(minDist, dist);
}
}
return minDist;
}
float fbm(vec2 p) {
float total = 0.0;
float amp = 0.5;
vec2 shift = vec2(1.0, 1.0);
for(int i=0; i<6; i++) {
total += worley(p) * amp;
p = p * 2.03 + shift;
amp *= 0.5;
shift *= 2.0;
}
return total;
}
/* Constantes realistas */
const float RADIUS = 0.06;
const float TRENCH_DEPTH = 0.65;
const float RIDGE_RATIO = 0.25;
const int TRAIL_STEPS = 200;
const float DT = 0.05;
const int SHADOW_STEPS = 32;
const float SHADOW_LEN = 0.3;
const float SMOOTHNESS = 0.7;
/* Posição da esfera com movimento complexo */
vec2 spherePos(float t){
float x = 0.5 + 0.35*sin(t*0.63 + sin(t*0.21));
float y = 0.5 + 0.35*cos(t*0.41 + cos(t*0.17));
return vec2(x, y);
}
/* Altura procedural com microdetalhes */
float baseHeight(vec2 st){
float h = 0.0;
float amp = 0.5;
vec2 q = st * 12.0;
for(int i=0;i<6;i++){
h += fbm(q) * amp;
q *= 2.1;
amp *= 0.45;
}
return h;
}
/* Perfil do sulco com detalhes */
float trenchAndRidge(float d){
float r = RADIUS * 1.5;
float x = clamp(d / r, 0.0, 1.0);
float trench = (1.0 - x*x) * (1.0 - x) * TRENCH_DEPTH;
float ridge = exp(-pow((d - RADIUS)*3.5 / RADIUS, 2.0)) * (TRENCH_DEPTH * RIDGE_RATIO);
return ridge - trench;
}
/* Deslocamento com memória */
float pathDisp(vec2 st){
float minD = 1e5;
for(int i=0;i<TRAIL_STEPS;i++){
float ti = u_time - float(i)*DT;
if(ti < 0.0) break;
vec2 p = spherePos(ti);
float d = length(st - p);
minD = min(minD, d);
}
return trenchAndRidge(minD);
}
/* Campo de altura com detalhes microscópicos */
float heightField(vec2 st){
float base = baseHeight(st);
// Detalhes microscópicos
float micro = 0.0;
for(int i=0; i<4; i++) {
micro += worley(st * pow(2.0, float(i)+5.0)) * pow(0.5, float(i));
}
micro *= 0.04;
return base + pathDisp(st) + micro;
}
/* Normais com anisotropia */
vec3 sandNormal(vec2 st){
vec2 e = vec2(1.0) / u_resolution;
float hL = heightField(st - vec2(e.x, 0.));
float hR = heightField(st + vec2(e.x, 0.));
float hD = heightField(st - vec2(0., e.y));
float hU = heightField(st + vec2(0., e.y));
vec3 N = normalize(vec3(hL - hR, hD - hU, 2.0));
// Rugosidade anisotrópica
float angle = sin(u_time * 0.5 + st.x * 10.0);
float bump = worley(st*40.0)*0.03 * angle;
float bL = worley((st-vec2(e.x,0.))*40.0)*0.03 * angle;
float bR = worley((st+vec2(e.x,0.))*40.0)*0.03 * angle;
float bD = worley((st-vec2(0.,e.y))*40.0)*0.03 * angle;
float bU = worley((st+vec2(0.,e.y))*40.0)*0.03 * angle;
vec3 N2 = normalize(vec3(bL - bR, bD - bU, 2.0));
return normalize(mix(N, N2, 0.6));
}
/* Cor da areia com variação física */
vec3 sandColor(float h){
vec3 dry = mix(
vec3(0.92,0.84,0.66),
vec3(0.85,0.77,0.60),
smoothstep(0.0, 1.0, sin(u_time * 0.1 + uv.x * 10.0))
);
vec3 damp = mix(
vec3(0.65,0.57,0.45),
vec3(0.58,0.50,0.38),
smoothstep(0.0, 1.0, cos(u_time * 0.1 + uv.y * 10.0))
);
float t = smoothstep(0.0,1.5,h);
vec3 base = mix(dry,damp,t);
// Grãos de areia
float grain = worley(uv*20.0 + sin(u_time * 0.2)) * 0.2;
base *= 1.0 + grain*0.15;
// Umidade realista
float moisture = smoothstep(0.5, -0.5, h);
base = mix(base, vec3(0.3,0.25,0.2), moisture * 0.4);
return base;
}
/* Sombras volumétricas */
float volumetricShadow(vec2 st, vec3 L) {
float stepLen = SHADOW_LEN / float(SHADOW_STEPS);
vec2 dUV = L.xy / L.z * stepLen;
float density = 0.0;
float currentH = heightField(st);
for(int i=1; i<=SHADOW_STEPS; i++) {
vec2 ss = st + dUV * float(i);
float h = heightField(ss);
float proj = currentH + L.z * float(i) * stepLen;
if(h > proj) {
float diff = (h - proj) * 2.0;
density += exp(-diff*diff*8.0) * 0.05;
}
}
return exp(-density * 8.0);
}
/* Reflexão dinâmica */
vec3 dynamicEnvReflection(vec2 st, vec3 N) {
vec3 ref = reflect(-vec3(0,0,1), N);
vec2 offset = ref.xy * 0.5 + 0.5;
// Parallax mapping fake
vec2 parallax = N.xy * 0.05;
vec3 layer1 = sandColor(baseHeight(offset * 1.0 + parallax));
vec3 layer2 = sandColor(baseHeight(offset * 0.7 + parallax*0.7));
vec3 layer3 = sandColor(baseHeight(offset * 0.4 + parallax*0.4));
return mix(layer1, mix(layer2, layer3, 0.5), 0.4);
}
/* Subsurface scattering */
vec3 subsurfaceScatter(vec2 st, vec3 N, vec3 L) {
float curvature = length(fwidth(N)) * 5.0;
float scatter = exp(-pow(abs(dot(L, N)), 1.5) * 8.0);
return mix(vec3(0.8,0.6,0.5), vec3(1.0), scatter) * curvature * 0.4;
}
/* Material PBR */
struct Material {
vec3 albedo;
float metallic;
float roughness;
float ao;
};
Material getMaterial(float h) {
Material m;
m.albedo = sandColor(h);
m.metallic = 0.0;
m.roughness = 0.65 + h * 0.25;
m.ao = clamp(1.0 + h*0.45, 0.2, 1.0);
return m;
}
/* Esfera refletora com detalhes */
vec3 mirrorShade(vec2 st){
vec3 V = vec3(0.0,0.0,1.0);
vec2 c = spherePos(u_time);
float z = sqrt(max(0.0, RADIUS*RADIUS - dot(st-c, st-c)));
vec3 N = normalize(vec3(st-c, z));
vec3 R = reflect(-V,N);
// Reflexão dinâmica
vec3 env = dynamicEnvReflection(R.xy/R.z, N);
// Componentes de iluminação
vec3 L = normalize(vec3(0.2,0.6,1.0));
float diff = max(dot(N,L),0.0);
float spec = pow(max(dot(reflect(-L,N),V),0.0),64.0);
float fres = pow(1.0 - max(dot(V,N),0.0),5.0);
// Subsurface scattering na esfera
vec3 sss = subsurfaceScatter(st, N, L) * 0.3;
return env*0.4 + env*0.6*fres + vec3(spec) + sss;
}
void main(){
vec2 c = spherePos(u_time);
float h = heightField(uv);
vec3 N = sandNormal(uv);
vec3 L = normalize(vec3(0.45,0.45,1.0));
float NdotL = max(dot(N,L), 0.0);
// Material PBR
Material mat = getMaterial(h);
// BRDF aproximado
vec3 H = normalize(L + vec3(0,0,1));
float NdotH = max(dot(N, H), 0.0);
float roughness = mat.roughness;
float spec = pow(NdotH, 1.0 / max(roughness, 0.1));
spec *= sqrt(1.0 - roughness);
// Sombras
float sh = volumetricShadow(uv, L);
// Subsurface scattering
vec3 sss = subsurfaceScatter(uv, N, L);
// Componentes de iluminação
vec3 diffuse = mat.albedo * NdotL * sh * 0.8;
vec3 specular = vec3(spec) * sh * 0.6;
// Reflexões
vec3 reflection = dynamicEnvReflection(uv, N) * 0.3;
// Cor base
vec3 color = diffuse + specular + sss + reflection;
// Oclusão ambiente
color *= mat.ao;
// LED de borda
float edge = min(min(uv.x,1.0-uv.x),min(uv.y,1.0-uv.y));
color += vec3(1.0,0.92,0.70)*edge*0.65;
// Mistura com esfera
float inside = step(length(uv-c), RADIUS);
color = mix(color, mirrorShade(uv), inside);
// Efeitos finais
color *= 1.0 + 0.05*sin(u_time * 2.0 + uv.x * 50.0); // Brilho dinâmico
color = mix(color, vec3(0.95,0.9,0.85), 0.05*edge); // Borda suave
out_color = vec4(color,1.0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment