<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>     
body {margin:0px; padding:0px; overflow: hidden}
let program;
let cnt = 0;

function setup() {
  pixelDensity(1);
  const canvas = createCanvas(windowWidth, windowHeight,WEBGL);
  rectMode(CENTER);
  noStroke();
  fill(1);
  program = createShader(vert,frag);
}

function draw() {
  const ang = (cnt ++) / 180 * PI;
  const lightPos = [cos(ang), 0, sin(ang)];
  
  shader(program);
  background(0);
  program.setUniform('res',[width,height]);
  program.setUniform('lightPos', lightPos);
  program.setUniform('roughness', 0.5);
  rect(0,0,width,height);
}

const vert=`
#ifdef GL_ES
precision highp float;
precision highp int;
#endif

#extension GL_OES_standard_derivatives : enable
attribute vec3 aPosition;
uniform mat4 uModelViewMatrix;
uniform mat4 uProjectionMatrix;
uniform mat3 uNormalMatrix;
void main() {
  gl_Position = uProjectionMatrix * uModelViewMatrix * vec4(aPosition, 1.0);
}`;




const frag=`
#ifdef GL_ES
precision highp float;
#endif

// https://learnopengl.com/PBR/Theory

#define PI 3.14159265358979323846

uniform vec2 res;
uniform vec3 lightPos;
uniform float roughness;
uniform float metallic;

vec3 env(vec3 dir) {
  vec3 crd = vec3(0.5) + normalize(dir) * 0.5;
  vec3 sky = vec3(0.5, 0.7, 1.0);
  vec3 sky2 = vec3(0.7, 0.3, 0.0);
  vec3 water = vec3(0.1, 0.3, 0.4);
  vec3 color = mix(water, sky2,
    smoothstep(0.4, 0.5, crd.y));
  color = mix(color, sky,
    smoothstep(0.3, 0.9, crd.y));
  float th = 0.1;
  color.rgb += (smoothstep(0.0, th, abs(dir.y)) - 1.0) * 0.2;
  
  float l = pow(max(0.0, dot(dir, lightPos)), 24.0);
  color += vec3(1.0, 0.8, 0.6) * l;
  color = pow(color.rgb, vec3(2.2));
  return clamp(color, vec3(0.0), vec3(1.0));
}

vec3 irradiance(vec3 N) {
  vec3 irradiance = vec3(0.0);   

  // tangent space calculation from origin point
  vec3 up    = vec3(0.0, 1.0, 0.0);
  vec3 right = normalize(cross(up, N));
  up         = normalize(cross(N, right));

  const float sampleDelta = 0.05 * PI;
  float nrSamples = 0.0;
  for(float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta)
  {
      for(float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta)
      {
          // spherical to cartesian (in tangent space)
          vec3 tangentSample = vec3(sin(theta) * cos(phi),  sin(theta) * sin(phi), cos(theta));
          // tangent space to world
          vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N; 

          irradiance += env(sampleVec).rgb * cos(theta) * sin(theta);
          nrSamples++;
      }
  }
  irradiance = PI * irradiance * (1.0 / float(nrSamples));
  return irradiance;
}

vec4 shade(vec3 V, vec3 P, vec3 N) {
  vec3 baseColor = vec3(1.0, 0.01, 0.01);
  vec3 diffuse = irradiance(N) * baseColor * 1.0;
  vec4 color = vec4(diffuse, 1.0);
  return color;
}

float SDF(vec3 p) {
    float radius = 0.25;
    return length(p)-radius;
}

vec3 normal(vec3 P) {
  vec2 h = vec2(0.001, 0.0);
  return normalize(vec3(SDF(P + h.xyy) - SDF(P - h.xyy),
                        SDF(P + h.yxy) - SDF(P - h.yxy),
                        SDF(P + h.yyx) - SDF(P - h.yyx)));
}

float rayHitDist(vec3 eye, vec3 rayDir) {
    float dist = 0.0;
    float threshold = 0.005;
    for(int i = 0 ; i < 16 ; ++i) {
        float d = SDF(eye + rayDir * dist);
        if(d < threshold) { return dist; }
        dist += d;
    }
    return -1.0;
}

void main(void)
{
  vec2 crd = (gl_FragCoord.xy - res * 0.5) / min(res.x, res.y);
  
  vec3 eye = vec3(0.0, 0.0, -2.5);
  vec3 V = normalize(eye - vec3(crd, 0.0));
    
  float dist = rayHitDist(eye, -V);
  
  vec4 color = vec4(env(-V), 1.0);
  if (dist >= 0.0) {
      vec3 P = eye - V * dist;
      vec3 N = normal(P);
      color = mix(color, shade(V, P, N), smoothstep(0.0, 0.1, dot(V, N)));
  }
  color.rgb = pow(color.rgb, vec3(1.0/2.2));
  gl_FragColor = color;
}`;

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.