Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Daniel-Abrecht/873253bdedb0a9fbcb91c6744a96c623 to your computer and use it in GitHub Desktop.
Save Daniel-Abrecht/873253bdedb0a9fbcb91c6744a96c623 to your computer and use it in GitHub Desktop.
Fur inequality rendering
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="canvas" width="512" height="512"></canvas>
<script type="x-shader/x-vertex">
uniform vec3 direction, ucamera;
attribute vec3 coordinates;
varying vec3 ray;
varying vec3 vcamera;
const vec3 up = vec3(0,1,0);
void main(void){
mat3 rotation = mat3(
-1,0,0,
0,0,1,
0,1,0
);
vec3 forward = normalize(direction);
if(forward != up){
vec3 left = normalize(cross(forward,up));
vec3 top = normalize(cross(left,forward));
rotation = mat3(left,top,forward);
}
ray = rotation * vec3(coordinates.xy,1);
vcamera = ucamera;
gl_Position = vec4(coordinates, 1.0);
}
</script>
<script type="x-shader/x-fragment">
precision mediump float;
varying vec3 ray;
varying vec3 vcamera;
const vec3 up = vec3(0,1,0);
const float depth = 5.;
const int resolution = 1000;
const int backtrack = 30;
const float displacement = 1.5;
const int overtrace = 2;
const float step = depth / float(resolution);
const float backtrack_step = step / float(backtrack);
bool fur(vec3 p){
const float n = 10.;
const float d = 0.75;
const float r = 0.5;
const float m = 3.;
const vec2 t = vec2(-0.6, 1.4);
p.xyz = p.zxy;
if(p.z < 0.)
return false;
p.xy += vec2(cos(p.z*m*3.14*2.),sin(p.z*m*3.14*2.))/(n*2.);
p.xy += p.z*p.z * t;
p.xy = mod(abs(p.xy) * n, 2.) - 1.;
return 1. > ( dot(p.xy,p.xy) + p.z/d*r) + (1.-r);
}
bool sphere(vec3 p){
return dot(p,p) < 1.;
}
bool plotfunc(vec3 p){
return fur(p);
}
float finetune_distance(vec3 ray, vec3 viewpoint, float distance){
float res = distance;
for(int i=0; i<backtrack*overtrace; i++){
float bdist = float(i)*backtrack_step;
vec3 position = ray * (distance-bdist);
if(plotfunc(position-viewpoint)){
res = distance-bdist;
}else{
return res;
}
}
return res;
}
float finetune_surface_distance(vec3 ray, vec3 viewpoint){
for(int i=0; i<backtrack*overtrace; i++){
float bdist = float(i)*backtrack_step;
vec3 position = ray * bdist;
if(plotfunc(position+viewpoint))
return bdist;
}
return 1000.;
}
float getDisplacement(vec3 ray, vec3 viewpoint){
if(plotfunc(viewpoint)){
return finetune_distance(ray,-viewpoint,0.);
}else{
return finetune_surface_distance(ray,viewpoint);
}
}
void main(void){
vec3 viewpoint = vcamera;
vec3 zray = normalize(ray);
vec3 xray = ray == up ? vec3(1,0,0) : normalize(cross(zray,up));
vec3 yray = normalize(cross(zray,xray));
float distance;
vec3 position;
bool found = false;
for(int i=0; i<resolution; i++){
distance = float(i)*step;
position = zray * distance;
if(plotfunc(position-viewpoint)){
found = true;
break;
}
}
if(!found)
discard;
distance = finetune_distance(zray,viewpoint,distance);
position = zray * distance;
float ld = getDisplacement(zray,position - viewpoint + xray * backtrack_step * displacement);
float rd = getDisplacement(zray,position - viewpoint - xray * backtrack_step * displacement);
float td = getDisplacement(zray,position - viewpoint + yray * backtrack_step * displacement);
float bd = getDisplacement(zray,position - viewpoint - yray * backtrack_step * displacement);
vec3 dir = vec3(ld-rd,td-bd,0) * float(backtrack) / ( float(step) * displacement);
if(dir.xy == vec2(0,0)){
dir.z = 1.;
}else{
dir.z = 1. / length(dir.xy);
}
dir = normalize(dir);
gl_FragColor = vec4(dir, 1);
}
</script>
<script>
var canvas = document.getElementById("canvas");
var gl = canvas.getContext("webgl");
var rx=0.14, ry=-0.15, x=0, y=-1.25, z=0, keys=[];
var vertices = [
-1, 1, 0,
-1,-1, 0,
1,-1, 0,
-1, 1, 0,
1,-1, 0,
1, 1, 0,
];
indices = [0,1,2,3,4,5];
var vertex_buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var Index_Buffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
var vertCode = document.querySelector('script[type="x-shader/x-vertex"]').textContent;
var fragCode = document.querySelector('script[type="x-shader/x-fragment"]').textContent;
var vertShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertShader, vertCode);
gl.compileShader(vertShader);
var compiled = gl.getShaderParameter(vertShader, gl.COMPILE_STATUS);
if(!compiled){
var compilationLog = gl.getShaderInfoLog(vertShader);
console.log('Vertex shader compile error: ' + compilationLog);
}
var fragShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragShader, fragCode);
gl.compileShader(fragShader);
var compiled = gl.getShaderParameter(fragShader, gl.COMPILE_STATUS);
if(!compiled){
var compilationLog = gl.getShaderInfoLog(fragShader);
console.log('Fragment shader compile error: ' + compilationLog);
}
var shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertShader);
gl.attachShader(shaderProgram, fragShader);
gl.linkProgram(shaderProgram);
gl.useProgram(shaderProgram);
gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer);
var coord = gl.getAttribLocation(shaderProgram, "coordinates");
gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(coord);
gl.disable(gl.DEPTH_TEST);
var direction = gl.getUniformLocation(shaderProgram, 'direction');
var ucamera = gl.getUniformLocation(shaderProgram, 'ucamera');
gl.viewport(0,0,canvas.width,canvas.height);
function draw(){
var dx = -Math.cos(ry*Math.PI*2)*Math.sin(rx*Math.PI*2),
dy = Math.sin(ry*Math.PI*2),
dz = Math.cos(rx*Math.PI*2)*Math.cos(ry*Math.PI*2);
if(keys[89]){
x += dx * 0.01;
y += dy * 0.01;
z += dz * 0.01;
}
if(keys[88]){
x -= dx * 0.01;
y -= dy * 0.01;
z -= dz * 0.01;
}
if(keys[37]) rx -= 0.01;
if(keys[39]) rx += 0.01;
if(keys[38]) ry -= 0.01;
if(keys[40]) ry += 0.01;
if(ry>0.25) ry=0.25;
if(ry<-0.25) ry=-0.25;
gl.clearColor(0.0, 0.0, 0.3, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.uniform3f(ucamera,x,y,z);
gl.uniform3f(direction,dx,dy,dz);
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
requestAnimationFrame(draw);
}
draw();
addEventListener("keydown",function(event){
keys[event.which] = true;
});
addEventListener("keyup",function(event){
keys[event.which] = false;
});
</script>
</body></html>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<canvas id="ra" width="512" height="512"></canvas>
<canvas id="rb" width="512" height="512"></canvas><br/>
<canvas id="rc" width="512" height="512"></canvas>
<br/>
<canvas id="rd" width="512" height="512"></canvas>
<canvas id="re" width="512" height="512"></canvas><br/>
<canvas id="rf" width="512" height="512"></canvas>
<script>
var rae = document.getElementById("ra");
var rac = rae.getContext("2d");
var rbe = document.getElementById("rb");
var rbc = rbe.getContext("2d");
var rce = document.getElementById("rc");
var rcc = rce.getContext("2d");
var rde = document.getElementById("rd");
var rdc = rde.getContext("2d");
var ree = document.getElementById("re");
var rec = ree.getContext("2d");
var rfe = document.getElementById("rf");
var rfc = rfe.getContext("2d");
function plot(f,w,h,x,y){
x = x || 0;
y = y || 0;
w = w || rae.width;
h = h || rae.height;
for(let j=0;j<h;j++){
let ry = j / h * 2 - 1;
for(let i=0;i<w;i++){
let rx = i / w * 2 - 1;
let green1 = 0;
let red1 = 0;
let green2 = 0;
let red2 = 0;
let green3 = 0;
let red3 = 0;
let d1 = -1;
let d2 = -1;
let d3 = -1;
for(let k=0;k<w;k++){
let rz = k / w * 2 - 1;
let r1 = Math.max(-1,Math.min(1,+f([rx,ry,-rz])));
let r2 = Math.max(-1,Math.min(1,+f([rz,ry,rx])));
let r3 = Math.max(-1,Math.min(1,+f([rx,rz,ry])));
if(r1 > 0){
green1 += r1;
if(d1 == -1)
d1 = 1-k/w;
}else{
red1 -= r1;
}
if(r2 > 0){
green2 += r2;
if(d2 == -1)
d2 = 1-k/w;
}else{
red2 -= r2;
}
if(r3 > 0){
green3 += r3;
if(d3 == -1)
d3 = 1-k/w;
}else{
red3 -= r3;
}
}
green1 /= w;
red1 /= w;
green2 /= w;
red2 /= w;
green3 /= w;
red3 /= w;
rac.fillStyle="rgb("+Math.round(red1*220+35)+","+Math.round(green1*220+35)+",0)";
rac.fillRect(i+x,j+y,1,1);
rbc.fillStyle="rgb("+Math.round(red2*220+35)+","+Math.round(green2*220+35)+",0)";
rbc.fillRect(i+x,j+y,1,1);
rcc.fillStyle="rgb("+Math.round(red3*220+35)+","+Math.round(green3*220+35)+",0)";
rcc.fillRect(i+x,j+y,1,1);
let c = x=>Math.max(Math.round((x*0.9+0.1)*255),0);
rdc.fillStyle="rgb("+c(d1)+","+c(d1)+","+c(d1)+")";
rdc.fillRect(i+x,j+y,1,1);
rec.fillStyle="rgb("+c(d2)+","+c(d2)+","+c(d2)+")";
rec.fillRect(i+x,j+y,1,1);
rfc.fillStyle="rgb("+c(d3)+","+c(d3)+","+c(d3)+")";
rfc.fillRect(i+x,j+y,1,1);
}
}
}
function q(x){return x*x;}
function fur([x,y,z],n,f,r,d){
if(z < -1) return;
z = z * 0.5 + 0.5;
x += Math.cos(z*f*Math.PI*2)/(n*2) * (1-r);
y += Math.sin(z*f*Math.PI*2)/(n*2) * (1-r);
x += z*z * 1;
y += z*z * -2;
x = Math.abs(x) * n % 2 - 1;
y = Math.abs(y) * n % 2 - 1;
return 1 > ( x*x + y*y + z/d*r) + (1-r);
}
function smile([x,y,z]){
x *= 6;
y *= 6;
y = -y;
return q(x)+q(y)<36 && q(x+2.5)+q(y-2.5)>2.25 && q(x-2.5)+q(y-2.5)>2.25 && q((y+3.5)/1.5-q(x/4))+q(x/4)>1;
}
plot(x=>fur(x,15,5,0.5,3/4));
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment