Bump Mapping with javascript and glsl

Bump Mapping with javascript and glsl




/**
* A class creating buffers for a textured box to render it with WebGL
*/
class RasterTextureBox

/**
* Class representing a 4x4 Matrix
*/
class Matrix

constructor(mat)
this.data = new Float32Array(16);
for (let row = 0; row < 4; row++)
for (let col = 0; col < 4; col++)
this.data[row * 4 + col] = mat[col * 4 + row];




getVal(row, col)
return this.data[col * 4 + row];


setVal(row, col, val)
this.data[col * 4 + row] = val;


static translation(translation)

let m = Matrix.identity();
m.setVal(0, 3, translation.x);
m.setVal(1, 3, translation.y);
m.setVal(2, 3, translation.z);
return m;


static rotation(axis, angle)

let m = Matrix.identity()
let sin = Math.sin(angle);
let cos = Math.cos(angle);
if (axis.x != 0)
m.setVal(1, 1, cos);
m.setVal(1, 2, -sin);
m.setVal(2, 1, sin);
m.setVal(2, 2, cos);
else if (axis.y != 0)
m.setVal(0, 0, cos);
m.setVal(0, 2, sin);
m.setVal(2, 0, -sin);
m.setVal(2, 2, cos);
else
m.setVal(0, 0, cos);
m.setVal(0, 1, -sin);
m.setVal(1, 0, sin);
m.setVal(1, 1, cos);

return m;


static scaling(scale)

let m = Matrix.identity();
m.setVal(0, 0, scale.x);
m.setVal(1, 1, scale.y);
m.setVal(2, 2, scale.z);
return m;


/**
* Constructs a lookat matrix
* @param Vector eye - The position of the viewer
* @param Vector center - The position to look at
* @param Vector up - The up direction
* @return Matrix The resulting lookat matrix
*/
static lookat(eye, center, up)

let fBig = center.sub(eye);

// Vom Eye zum Center Punkt
let f = fBig.normalised();

// UP-Vektor
let upNorm = up.normalised();

// Kreuzprodukt
let s = f.cross(upNorm);

let u = s.normalised().cross(f);

// s, u und f sind die Vektoren des Kamerakoordinatensystems

// Lookat Matrix, 3x3 betrifft Rotation und Skalierung
let mat = new Matrix([
s.x, s.y, s.z, 0,
u.x, u.y, u.z, 0, -f.x, -f.y, -f.z, 0,
0, 0, 0, 1
]);

// Noch weitere Berechnungen? Translation
let trans = Matrix.translation(eye.mul(-1));
mat = mat.mul(trans);
return mat;



static frustum(left, right, bottom, top, near, far)
// TODO [exercise 9]
const n2 = 2 * near;
const rpl = right + left;
const rml = right - left;
const tpb = top + bottom;
const tmb = top - bottom;
const fpn = far + near;
const fmn = far - near;
const n2f = n2 * far;
return new Matrix([
n2 / rml, 0, rpl / rml, 0,
0, n2 / tmb, tpb / tmb, 0,
0, 0, -fpn / fmn, -n2f / fmn,
0, 0, -1, 0
]);




static perspective(fovy, aspect, near, far)

// frustum Methode verwenden. Foliensatz 10

const top = near * Math.tan((Math.PI / 180) * (fovy / 2));
const bottom = -top;
const right = top * aspect;
const left = -right;

return Matrix.frustum(left, right, bottom, top, near, far);



/**
* Returns the identity matrix
*/
static identity()
return new Matrix([
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
]);


mul(other)

if (other instanceof Matrix)
// [exercise 7]
let m = Matrix.identity();
for (let row = 0; row < 4; row++)
for (let col = 0; col < 4; col++)
let sum = 0;
for (let i = 0; i < 4; i++)
sum += this.getVal(row, i) * other.getVal(i, col);

m.setVal(row, col, sum);


return m;
else
let v = [0, 0, 0, 0];
for (let row = 0; row < 4; row++)
for (let i = 0; i < 4; i++)
v[row] += this.getVal(row, i) * other.valueOf()[i];


return new Vector(v[0], v[1], v[2], v[3]);





transpose()
let m = Matrix.identity();
for (let row = 0; row < 4; row++)
for (let col = 0; col < 4; col++)
m.setVal(row, col, this.getVal(col, row));


return m;




invert()
let mat = this.data;
let dst = new Float32Array(16); //ret.getValues();
let tmp = new Float32Array(12);

/* temparray for pairs */
let src = new Float32Array(16); //new float[16];

/* array of transpose source matrix */
let det;

for (let i = 0; i < 4; i++)
src[i] = mat[i * 4];
src[i + 4] = mat[i * 4 + 1];
src[i + 8] = mat[i * 4 + 2];
src[i + 12] = mat[i * 4 + 3];


tmp[0] = src[10] * src[15];
tmp[1] = src[11] * src[14];
tmp[2] = src[9] * src[15];
tmp[3] = src[11] * src[13];
tmp[4] = src[9] * src[14];
tmp[5] = src[10] * src[13];
tmp[6] = src[8] * src[15];
tmp[7] = src[11] * src[12];
tmp[8] = src[8] * src[14];
tmp[9] = src[10] * src[12];
tmp[10] = src[8] * src[13];
tmp[11] = src[9] * src[12];

dst[0] = tmp[0] * src[5] + tmp[3] * src[6] + tmp[4] * src[7];
dst[0] -= tmp[1] * src[5] + tmp[2] * src[6] + tmp[5] * src[7];
dst[1] = tmp[1] * src[4] + tmp[6] * src[6] + tmp[9] * src[7];
dst[1] -= tmp[0] * src[4] + tmp[7] * src[6] + tmp[8] * src[7];
dst[2] = tmp[2] * src[4] + tmp[7] * src[5] + tmp[10] * src[7];
dst[2] -= tmp[3] * src[4] + tmp[6] * src[5] + tmp[11] * src[7];
dst[3] = tmp[5] * src[4] + tmp[8] * src[5] + tmp[11] * src[6];
dst[3] -= tmp[4] * src[4] + tmp[9] * src[5] + tmp[10] * src[6];
dst[4] = tmp[1] * src[1] + tmp[2] * src[2] + tmp[5] * src[3];
dst[4] -= tmp[0] * src[1] + tmp[3] * src[2] + tmp[4] * src[3];
dst[5] = tmp[0] * src[0] + tmp[7] * src[2] + tmp[8] * src[3];
dst[5] -= tmp[1] * src[0] + tmp[6] * src[2] + tmp[9] * src[3];
dst[6] = tmp[3] * src[0] + tmp[6] * src[1] + tmp[11] * src[3];
dst[6] -= tmp[2] * src[0] + tmp[7] * src[1] + tmp[10] * src[3];
dst[7] = tmp[4] * src[0] + tmp[9] * src[1] + tmp[10] * src[2];
dst[7] -= tmp[5] * src[0] + tmp[8] * src[1] + tmp[11] * src[2];

tmp[0] = src[2] * src[7];
tmp[1] = src[3] * src[6];
tmp[2] = src[1] * src[7];
tmp[3] = src[3] * src[5];
tmp[4] = src[1] * src[6];
tmp[5] = src[2] * src[5];
tmp[6] = src[0] * src[7];
tmp[7] = src[3] * src[4];
tmp[8] = src[0] * src[6];
tmp[9] = src[2] * src[4];
tmp[10] = src[0] * src[5];
tmp[11] = src[1] * src[4];

dst[8] = tmp[0] * src[13] + tmp[3] * src[14] + tmp[4] * src[15];
dst[8] -= tmp[1] * src[13] + tmp[2] * src[14] + tmp[5] * src[15];
dst[9] = tmp[1] * src[12] + tmp[6] * src[14] + tmp[9] * src[15];
dst[9] -= tmp[0] * src[12] + tmp[7] * src[14] + tmp[8] * src[15];
dst[10] = tmp[2] * src[12] + tmp[7] * src[13] + tmp[10] * src[15];
dst[10] -= tmp[3] * src[12] + tmp[6] * src[13] + tmp[11] * src[15];
dst[11] = tmp[5] * src[12] + tmp[8] * src[13] + tmp[11] * src[14];
dst[11] -= tmp[4] * src[12] + tmp[9] * src[13] + tmp[10] * src[14];
dst[12] = tmp[2] * src[10] + tmp[5] * src[11] + tmp[1] * src[9];
dst[12] -= tmp[4] * src[11] + tmp[0] * src[9] + tmp[3] * src[10];
dst[13] = tmp[8] * src[11] + tmp[0] * src[8] + tmp[7] * src[10];
dst[13] -= tmp[6] * src[10] + tmp[9] * src[11] + tmp[1] * src[8];
dst[14] = tmp[6] * src[9] + tmp[11] * src[11] + tmp[3] * src[8];
dst[14] -= tmp[10] * src[11] + tmp[2] * src[8] + tmp[7] * src[9];
dst[15] = tmp[10] * src[10] + tmp[4] * src[8] + tmp[9] * src[9];
dst[15] -= tmp[8] * src[9] + tmp[11] * src[10] + tmp[5] * src[8];

det = src[0] * dst[0] + src[1] * dst[1] + src[2] * dst[2] + src[3] * dst[3];

if (det == 0.0)
throw new Error("singular matrix is not invertible");


/* calculate matrix inverse */
det = 1 / det;

for (let j = 0; j < 16; j++)
dst[j] *= det;


let ret = Matrix.identity();
ret.data = dst;
return ret;



/**
* Class representing a vector in 4D space
*/
class Vector

/**
* Create a vector
* @param number x - The x component
* @param number y - The y component
* @param number z - The z component
* @param number w - The w component
* @return number The resulting vector
*/
constructor(x, y, z, w)
this.data = [x, y, z, w];


//has getter and setter

add(other)
return new Vector(
this.x + other.x,
this.y + other.y,
this.z + other.z,
this.w + other.w
);


sub(other)
return new Vector(
this.x - other.x,
this.y - other.y,
this.z - other.z,
this.w - other.w
);


mul(other)
return new Vector(
this.x * other,
this.y * other,
this.z * other,
this.w
);


div(other)
return new Vector(
this.x / other,
this.y / other,
this.z / other,
this.w
);


dot(other)
if (other instanceof Vector)
return this.x * other.x + this.y * other.y + this.z * other.z;
else
throw new Error("Dot product only works with vectors!");



cross(other)
if (other instanceof Vector)
return new Vector(
this.y * other.z - this.z * other.y,
this.z * other.x - this.x * other.z,
this.x * other.y - this.y * other.x,
0
);
else
throw new Error("Dot product only works with vectors!");



valueOf()
return this.data;


normalised()
const l = this.length;
return this.div(l);


equals(other) Math.abs(this.w - other.w) <= Number.EPSILON)
);


get length()
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);



/**
* Class representing a Node in a Scenegraph
*/
class Node
/**
* Accepts a visitor according to the visitor pattern
* @param Visitor visitor - The visitor
*/
accept(visitor)


/**
* Class representing a GroupNode in the Scenegraph.
* A GroupNode holds a transformation and is able
* to have child nodes attached to it.
* @extends Node
*/
class GroupNode extends Node
/**
* Constructor
* @param Matrix mat - A matrix describing the node's transformation
*/
constructor(mat)
super();
this.matrix = mat;
// TODO [exercise 8]
this.children = ;


/**
* Accepts a visitor according to the visitor pattern
* @param Visitor visitor - The visitor
*/
accept(visitor)
// TODO [exercise 8]
visitor.visitGroupNode(this);


/**
* Adds a child node
* @param Node childNode - The child node to add
*/
add(childNode)
// TODO [exercise 8]
this.children.push(childNode);



/**
* Class representing a Textured Axis Aligned Box in the Scenegraph
* @extends Node
*/
class TextureBoxNode extends Node

constructor(minPoint, maxPoint, texture)
super();
this.minPoint = minPoint;
this.maxPoint = maxPoint;
this.texture = texture;


accept(visitor)
// TODO [exercise 8]
visitor.visitTextureBoxNode(this);


//Texture Fragment Shader

precision mediump float;

uniform sampler2D sampler;
varying vec2 v_texCoord;

void main( void )
//gl_FragColor = vec4( 0.0, 0.0, 0.5, 1.0 );

// Read fragment color from texture
// TODO [exercise 9]
gl_FragColor = texture2D(sampler, vec2(v_texCoord.s, v_texCoord.t));


//Texture Vertex Shader

attribute vec3 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;

void main()
gl_Position = P * V * M * vec4( a_position, 1.0 );
v_texCoord = a_texCoord;


// Phong Vertex Shader

attribute vec3 a_position;
attribute vec3 a_normal;

// Pass color as attribute and forward it
// to the fragment shader
attribute vec4 a_color;

uniform mat4 M;
uniform mat4 V;
uniform mat4 P;
uniform mat4 N; // normal matrix

varying vec3 v_normal;

// Pass the vertex position in view space
// to the fragment shader
// TODO [exercise 9]

varying vec4 v_position;
varying vec4 v_color;

void main()
gl_Position = P * V * M * vec4( a_position, 1.0 );

// Pass the color and transformed vertex position through
v_position = gl_Position;
v_color = a_color;

v_normal = (N * vec4(a_normal, 0)).xyz;


//Phong Fragment Shader
//precision mediump float;
// TODO [exercise 5]
//void main( void )
//gl_FragColor = vec4( 0.0, 0.0, 0.5, 1.0 );
// TODO [exercise 5]
//

// Wird mindestens einmal pro Pixel ausgefuehrt

precision mediump float;
// TODO [exercise 5]
varying vec4 v_color;
varying vec4 v_position;
varying vec3 v_normal;

const vec3 lightPos = vec3(0.2,-1.0,-1.0);
const float shininess = 16.0;

const float k_a = 1.0;
const float k_d = 0.6;
const float k_s = 0.3;

// Farbe von Vertex shader durchreichen und Interpolieren
void main( void )
// Rot, Gruen, Blau, Alpha
//gl_FragColor = vec4( 0.0, 0.0, 0.5, 1.0 );
// TODO [exercise 5]
vec3 vertPos = vec3(v_position) / v_position.w;
vec3 N = normalize(v_normal);
vec3 L = normalize(lightPos - vertPos);

vec4 L_j = vec4(1,1,1,1);

vec4 diffuse = L_j * max(dot(N, L), 0.0);

vec3 R = reflect(-L, N);
vec3 V = normalize(-vertPos);
float specAngle = max(dot(R, V), 0.0);
vec4 specular = L_j * pow(specAngle, shininess);

vec4 color = vec4(k_a * v_color + k_d * diffuse + k_s * specular);
gl_FragColor = color;


<!DOCTYPE html>
<html>

<head>
<meta charset="utf-8" />
<title>ICG-11 Animation</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u"
crossorigin="anonymous">
</head>

<body>

<div class="container text-center">
<h1>ICG Animation</h1>
<hr>
<p>Implement a Rasteriser with WebGL using a Scenegraph.</p>
<canvas id="rasteriser" width="500" height="500"></canvas>
<script src="vector.js"></script>
<script src="raster-texture-box.js"></script>
<script src="matrix.js"></script>
<script src="nodes.js"></script>
<script src="rastervisitor.js"></script>
<script src="shader.js"></script>
<script src="animation-nodes.js"></script>
<script>
const canvas = document.getElementById("rasteriser");
const gl = canvas.getContext("webgl");

// construct scene graph
const sg = new GroupNode(Matrix.scaling(new Vector(0.2, 0.2, 0.2)));
const gn1 = new GroupNode(Matrix.translation(new Vector(1, 1, 0)));
sg.add(gn1);

let gn2 = new GroupNode(Matrix.translation(new Vector(-.7, -0.4, .1)));
sg.add(gn2);
const cube = new TextureBoxNode(
new Vector(-1, -1, -1, 1),
new Vector(1, 1, 1, 1),
'diffuse.png'
);
gn2.add(cube);

// setup for rendering
const setupVisitor = new RasterSetupVisitor(gl);
setupVisitor.setup(sg);

const visitor = new RasterVisitor(gl);

let camera =
eye: new Vector(-.5, .5, -1, 1),
center: new Vector(0, 0, 0, 1),
up: new Vector(0, 1, 0, 0),
fovy: 60,
aspect: canvas.width / canvas.height,
near: 0.1,
far: 100
;

const phongShader = new Shader(gl,
"phong-vertex-perspective-shader.glsl",
"phong-fragment-shader.glsl"
);
visitor.shader = phongShader;
const textureShader = new Shader(gl,
"texture-vertex-perspective-shader.glsl",
"texture-fragment-shader.glsl"
);
visitor.textureshader = textureShader;

let animationNodes = [
new RotationNode(gn2, new Vector(0, 0, 1))
];

function simulate(deltaT)
for (animationNode of animationNodes)
animationNode.simulate(deltaT);



let lastTimestamp = performance.now();

function animate(timestamp)
simulate(timestamp - lastTimestamp);
visitor.render(sg, camera);
lastTimestamp = timestamp;
window.requestAnimationFrame(animate);

Promise.all(
[textureShader.load(), phongShader.load()]
).then(x =>
window.requestAnimationFrame(animate)
);

</script>
</div>
</body>

</html>



Hey there I`m trying since a while now to add a second texture1 to my cube and do some bump mapping. But I am a progam beginner, so its kinda hard for me. All my maths for the matrix and vector are in the same named js.files. I also have to kinds of shaders, the texture and the phong shader. Now everyone says I have to calculate my normals, but how do I do that? And where?
Looking forward for your help!





You have to be more specific. What exactly is the question? Please add the relevant parts of the code to the question and please read How to create a Minimal, Complete, and Verifiable example and How do I ask a good question?
– Rabbid76
Aug 23 at 14:57





I do not see any bump/normal mapping in your fragment shader. The idea is to get the normal from texture apply transformation on it and use that for the lighting computations. You are just outputting texture as color. Take a look at this: Normal mapping gone horribly wrong but it is C++/GLSL but the shaders should be similar. Also You need TBN (tangent,binormal,normal) matrix for each face (triangle) in order to be capable of converting the texture normal to real normal I do not see you passing it from vertex shade nor computing it on your own
– Spektre
Aug 24 at 14:11





start small first just add lighting and normals ... if working only then go for bump/normal mapping.
– Spektre
Aug 24 at 14:12




1 Answer
1



For any normal mapping, bump mapping or displacement mapping, you'll at least need the normal vectors of the geometry (vertices) in view space and you have to pass it to the fragment shader, because the normal of the fragment is replaced by the normal vector of the bump map, which is transformed form (co-)tangentspace to view space.



See also Normal, Parallax and Relief mapping

and an Example which compares different techniques




var readInput = true;
function changeEventHandler(event)
readInput = true;


(function loadscene()

var gl, progDraw, vp_size, camera;
var bufCube = ;
var clip = 0.0;
var displacement = 0.0;
var tex_unit = 1;
var height_map_unit = 2;
var height_map_size = [0, 0];

function render(deltaMS)

if (readInput)
clip = (document.getElementById("clip").value - 50) / 50;
displacement = document.getElementById("displacement").value / 400;
quality = document.getElementById("quality").value / 100;
document.getElementById("fps_val").innerHTML = getFPS(deltaMS).toFixed(1);


camera.Update( vp_size );

gl.viewport(0, 0, vp_size[0], vp_size[1]);
gl.enable(gl.DEPTH_TEST);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT

function resize()
vp_size = [window.innerWidth, window.innerHeight]
canvas.width = vp_size[0];
canvas.height = vp_size[1];


function initScene()

canvas = document.getElementById("canvas");
gl = canvas.getContext("experimental-webgl");
if (!gl)
return null;
var standard_derivatives = gl.getExtension("OES_standard_derivatives"); // dFdx, dFdy
if (!standard_derivatives)
alert('no standard derivatives support (no dFdx, dFdy)');

progDraw = ShaderProgram.Create(
[ source: "draw-shader-vs", stage: gl.VERTEX_SHADER ,
source: "draw-shader-fs", stage: gl.FRAGMENT_SHADER
]);
if (!progDraw.progObj)
return null;
progDraw.inPos = ShaderProgram.AttributeIndex(progDraw, "inPos");
progDraw.inNV = ShaderProgram.AttributeIndex(progDraw, "inNV");
progDraw.inUV = ShaderProgram.AttributeIndex(progDraw, "inUV");
progDraw.inCol = ShaderProgram.AttributeIndex(progDraw, "inCol");

// create cube
var cubePos = [
-1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, 1.0, -1.0, -1.0, 1.0, -1.0];
var cubeCol = [1.0, 0.0, 0.0, 1.0, 0.5, 0.0, 1.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0];
var cubeHlpInx = [0, 1, 2, 3, 1, 5, 6, 2, 5, 4, 7, 6, 4, 0, 3, 7, 3, 2, 6, 7, 1, 0, 4, 5];
var cubePosData = ;
for (var i = 0; i < cubeHlpInx.length; ++i)
cubePosData.push(cubePos[cubeHlpInx[i] * 3], cubePos[cubeHlpInx[i] * 3 + 1], cubePos[cubeHlpInx[i] * 3 + 2]);

var cubeNVData = ;
for (var i1 = 0; i1 < cubeHlpInx.length; i1 += 4)
var nv = [0, 0, 0];
for (i2 = 0; i2 < 4; ++i2)
var i = i1 + i2;
nv[0] += cubePosData[i * 3]; nv[1] += cubePosData[i * 3 + 1]; nv[2] += cubePosData[i * 3 + 2];

for (i2 = 0; i2 < 4; ++i2)
cubeNVData.push(nv[0], nv[1], nv[2]);

var cubeColData = ;
for (var is = 0; is < 6; ++is)
for (var ip = 0; ip < 4; ++ip)
cubeColData.push(cubeCol[is * 3], cubeCol[is * 3 + 1], cubeCol[is * 3 + 2]);


var cubeTexData =
for (var i = 0; i < 6; ++i)
cubeTexData.push(0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0);

var cubeInxData = ;
for (var i = 0; i < cubeHlpInx.length; i += 4)
cubeInxData.push(i, i + 1, i + 2, i, i + 2, i + 3);

bufCube = VertexBuffer.Create(
[ data: cubePosData, attrSize: 3, attrLoc: progDraw.inPos ,
data: cubeNVData, attrSize: 3, attrLoc: progDraw.inNV ,
data: cubeTexData, attrSize: 2, attrLoc: progDraw.inUV ,
data: cubeColData, attrSize: 3, attrLoc: progDraw.inCol ],
cubeInxData);

Texture.LoadTexture2D(tex_unit, "https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/example_1_texture.bmp");
Texture.LoadTexture2D(height_map_unit, "https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/example_1_heightmap.bmp");

window.onresize = resize;
resize();
camera = new Camera( [0, 2.5, 0.0], [0, 0, 0], [0, 0, 1], 90, vp_size, 0.5, 100 );
requestAnimationFrame(render);


var times = ;
var timeSum = 0;
var actIndex = 0;
var maxTimes = 100;
var first = true;
var prevTime = 0;
function getFPS(delta_sum_MS)
if (first)

prevTime = delta_sum_MS;
first = false;
return 0;

var deltaMS = delta_sum_MS - prevTime;
prevTime = delta_sum_MS;
timeSum = timeSum + deltaMS;
if ( times.length < maxTimes )
times.push(deltaMS);
else
timeSum = timeSum - times[actIndex];
times[actIndex] = deltaMS;
actIndex = actIndex < maxTimes-1 ? actIndex+1 : 0;

return 1000 * times.length / timeSum;


function Fract(val)
return val - Math.trunc(val);

function CalcAng(deltaTime, intervall)
return Fract(deltaTime / (1000 * intervall)) * 2.0 * Math.PI;

function CalcMove(deltaTime, intervall, range)
var pos = Fract(deltaTime / (1000 * intervall)) * 2.0
var pos = pos < 1.0 ? pos : (2.0 - pos)
return range[0] + (range[1] - range[0]) * pos;

function EllipticalPosition(a, b, angRag)
var a_b = a * a - b * b
var ea = (a_b <= 0) ? 0 : Math.sqrt(a_b);
var eb = (a_b >= 0) ? 0 : Math.sqrt(-a_b);
return [a * Math.sin(angRag) - ea, b * Math.cos(angRag) - eb, 0];


function IdentityMat44() return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];

function RotateAxis(matA, angRad, axis)
var aMap = [ [1, 2], [2, 0], [0, 1] ];
var a0 = aMap[axis][0], a1 = aMap[axis][1];
var sinAng = Math.sin(angRad), cosAng = Math.cos(angRad);
var matB = matA.slice(0);
for ( var i = 0; i < 3; ++ i )
matB[a0*4+i] = matA[a0*4+i] * cosAng + matA[a1*4+i] * sinAng;
matB[a1*4+i] = matA[a0*4+i] * -sinAng + matA[a1*4+i] * cosAng;

return matB;


function Rotate(matA, angRad, axis)
var s = Math.sin(angRad), c = Math.cos(angRad);
var x = axis[0], y = axis[1], z = axis[2];
matB = [
x*x*(1-c)+c, x*y*(1-c)-z*s, x*z*(1-c)+y*s, 0,
y*x*(1-c)+z*s, y*y*(1-c)+c, y*z*(1-c)-x*s, 0,
z*x*(1-c)-y*s, z*y*(1-c)+x*s, z*z*(1-c)+c, 0,
0, 0, 0, 1 ];
return Multiply(matA, matB);


function Multiply(matA, matB)
matC = IdentityMat44();
for (var i0=0; i0<4; ++i0 )
for (var i1=0; i1<4; ++i1 )
matC[i0*4+i1] = matB[i0*4+0] * matA[0*4+i1] + matB[i0*4+1] * matA[1*4+i1] + matB[i0*4+2] * matA[2*4+i1] + matB[i0*4+3] * matA[3*4+i1]
return matC;


function Cross(a, b) return [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0], 0.0];
function Dot(a, b) return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
function Normalize(v)
var len = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
return [v[0] / len, v[1] / len, v[2] / len];


Camera = function( pos, target, up, fov_y, vp, near, far )
this.Time = function() return Date.now();
this.pos = pos;
this.target = target;
this.up = up;
this.fov_y = fov_y;
this.vp = vp;
this.near = near;
this.far = far;
this.orbit_mat = this.current_orbit_mat = this.model_mat = this.current_model_mat = IdentityMat44();
this.mouse_drag = this.auto_spin = false;
this.auto_rotate = true;
this.mouse_start = [0, 0];
this.mouse_drag_axis = [0, 0, 0];
this.mouse_drag_angle = 0;
this.mouse_drag_time = 0;
this.drag_start_T = this.rotate_start_T = this.Time();
this.Ortho = function()
var fn = this.far + this.near;
var f_n = this.far - this.near;
var w = this.vp[0];
var h = this.vp[1];
return [
2/w, 0, 0, 0,
0, 2/h, 0, 0,
0, 0, -2/f_n, 0,
0, 0, -fn/f_n, 1 ];
;
this.Perspective = function()
var n = this.near;
var f = this.far;
var fn = f + n;
var f_n = f - n;
var r = this.vp[0] / this.vp[1];
var t = 1 / Math.tan( Math.PI * this.fov_y / 360 );
return [
t/r, 0, 0, 0,
0, t, 0, 0,
0, 0, -fn/f_n, -1,
0, 0, -2*f*n/f_n, 0 ];
;
this.LookAt = function()
var mz = Normalize( [ this.pos[0]-this.target[0], this.pos[1]-this.target[1], this.pos[2]-this.target[2] ] );
var mx = Normalize( Cross( this.up, mz ) );
var my = Normalize( Cross( mz, mx ) );
var tx = Dot( mx, this.pos );
var ty = Dot( my, this.pos );
var tz = Dot( [-mz[0], -mz[1], -mz[2]], this.pos );
return [mx[0], my[0], mz[0], 0, mx[1], my[1], mz[1], 0, mx[2], my[2], mz[2], 0, tx, ty, tz, 1];
;
this.Orbit = function()
return Multiply(this.LookAt(), this.OrbitMatrix());
;
this.OrbitMatrix = function()
return (this.mouse_drag ;
this.AutoModelMatrix = function()
return this.auto_rotate ? Multiply(this.current_model_mat, this.model_mat) : this.model_mat;
;
this.Update = function(vp_size)
if (vp_size)
this.vp = vp_size;
var current_T = this.Time();
this.current_model_mat = IdentityMat44()
if (this.mouse_drag)
this.current_orbit_mat = Rotate(IdentityMat44(), this.mouse_drag_angle, this.mouse_drag_axis);
else if (this.auto_rotate)
if (this.auto_spin )
if (this.mouse_drag_time > 0 )
var angle = this.mouse_drag_angle * (current_T - this.rotate_start_T) / this.mouse_drag_time;
this.current_orbit_mat = Rotate(IdentityMat44(), angle, this.mouse_drag_axis);

else
var auto_angle_x = Fract( (current_T - this.rotate_start_T) / 13000.0 ) * 2.0 * Math.PI;
var auto_angle_y = Fract( (current_T - this.rotate_start_T) / 17000.0 ) * 2.0 * Math.PI;
this.current_model_mat = RotateAxis( this.current_model_mat, auto_angle_x, 0 );
this.current_model_mat = RotateAxis( this.current_model_mat, auto_angle_y, 1 );


;
this.ChangeMotionMode = function(drag, spin, auto ) ;
this.OnMouseDown = function( event ) ;
this.OnMouseUp = function( event )
if (event.button == 0) // left button
this.ChangeMotionMode( false, true, true );
else if (event.button == 1) // middle button
this.ChangeMotionMode( false, false, !this.auto_rotate );

;
this.OnMouseMove = function( event )
var dx = (event.clientX-this.mouse_start[0]) / this.vp[0];
var dy = (event.clientY-this.mouse_start[1]) / this.vp[1];
var len = Math.sqrt(dx*dx + dy*dy);
if (this.mouse_drag && len > 0)
this.mouse_drag_angle = Math.PI*len;
this.mouse_drag_axis = [dy/len, 0, -dx/len];
this.mouse_drag_time = this.Time() - this.drag_start_T;

;

this.domElement = document;
var cam = this;
this.domElement.addEventListener( 'mousedown', function(e) cam.OnMouseDown(event) , false );
this.domElement.addEventListener( 'mouseup', function(e) cam.OnMouseUp(e) , false );
this.domElement.addEventListener( 'mousemove', function(e) cam.OnMouseMove(e) , false );


var Texture = ;
Texture.HandleLoadedTexture2D = function (texture, flipY)
gl.activeTexture(gl.TEXTURE0 + texture.unit);
gl.bindTexture(gl.TEXTURE_2D, texture.obj);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
if (flipY != undefined && flipY == true)
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
if (texture.unit == height_map_unit)
height_map_size = [texture.image.width, texture.image.height];
return texture;

Texture.LoadTexture2D = function (unit, name)
var texture = ;
texture.obj = gl.createTexture();
texture.unit = unit;
texture.image = new Image();
texture.image.setAttribute('crossorigin', 'anonymous');
texture.image.onload = function ()
Texture.HandleLoadedTexture2D(texture, true)

texture.image.src = name;
return texture;


var ShaderProgram = ;
ShaderProgram.Create = function (shaderList)
var shaderObjs = ;
for (var i_sh = 0; i_sh < shaderList.length; ++i_sh)
var shderObj = this.CompileShader(shaderList[i_sh].source, shaderList[i_sh].stage);
if (shderObj == 0)
return 0;
shaderObjs.push(shderObj);

var prog =
prog.progObj = this.LinkProgram(shaderObjs)
if (prog.progObj)
prog.attribIndex = ;
var noOfAttributes = gl.getProgramParameter(prog.progObj, gl.ACTIVE_ATTRIBUTES);
for (var i_n = 0; i_n < noOfAttributes; ++i_n)
var name = gl.getActiveAttrib(prog.progObj, i_n).name;
prog.attribIndex[name] = gl.getAttribLocation(prog.progObj, name);

prog.unifomLocation = ;
var noOfUniforms = gl.getProgramParameter(prog.progObj, gl.ACTIVE_UNIFORMS);
for (var i_n = 0; i_n < noOfUniforms; ++i_n)
var name = gl.getActiveUniform(prog.progObj, i_n).name;
prog.unifomLocation[name] = gl.getUniformLocation(prog.progObj, name);


return prog;

ShaderProgram.AttributeIndex = function (prog, name) return prog.attribIndex[name];
ShaderProgram.UniformLocation = function (prog, name) return prog.unifomLocation[name];
ShaderProgram.Use = function (prog) gl.useProgram(prog.progObj);
ShaderProgram.SetUniformI1 = function (prog, name, val) if (prog.unifomLocation[name]) gl.uniform1i(prog.unifomLocation[name], val);
ShaderProgram.SetUniformF1 = function (prog, name, val) if (prog.unifomLocation[name]) gl.uniform1f(prog.unifomLocation[name], val);
ShaderProgram.SetUniformF2 = function (prog, name, arr) if (prog.unifomLocation[name]) gl.uniform2fv(prog.unifomLocation[name], arr);
ShaderProgram.SetUniformF3 = function (prog, name, arr) if (prog.unifomLocation[name]) gl.uniform3fv(prog.unifomLocation[name], arr);
ShaderProgram.SetUniformF4 = function (prog, name, arr) if (prog.unifomLocation[name]) gl.uniform4fv(prog.unifomLocation[name], arr);
ShaderProgram.SetUniformM33 = function (prog, name, mat) if (prog.unifomLocation[name]) gl.uniformMatrix3fv(prog.unifomLocation[name], false, mat);
ShaderProgram.SetUniformM44 = function (prog, name, mat) if (prog.unifomLocation[name]) gl.uniformMatrix4fv(prog.unifomLocation[name], false, mat);
ShaderProgram.CompileShader = function (source, shaderStage)
var shaderScript = document.getElementById(source);
if (shaderScript)
source = shaderScript.text;
var shaderObj = gl.createShader(shaderStage);
gl.shaderSource(shaderObj, source);
gl.compileShader(shaderObj);
var status = gl.getShaderParameter(shaderObj, gl.COMPILE_STATUS);
if (!status) alert(gl.getShaderInfoLog(shaderObj));
return status ? shaderObj : null;

ShaderProgram.LinkProgram = function (shaderObjs)
var prog = gl.createProgram();
for (var i_sh = 0; i_sh < shaderObjs.length; ++i_sh)
gl.attachShader(prog, shaderObjs[i_sh]);
gl.linkProgram(prog);
status = gl.getProgramParameter(prog, gl.LINK_STATUS);
if (!status) alert("Could not initialise shaders");
gl.useProgram(null);
return status ? prog : null;


var VertexBuffer = ;
VertexBuffer.Create = function (attributes, indices)
var buffer = ;
buffer.buf = ;
buffer.attr =
for (var i = 0; i < attributes.length; ++i)
buffer.buf.push(gl.createBuffer());
buffer.attr.push( size: attributes[i].attrSize, loc: attributes[i].attrLoc );
gl.bindBuffer(gl.ARRAY_BUFFER, buffer.buf[i]);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(attributes[i].data), gl.STATIC_DRAW);

buffer.inx = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffer.inx);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
buffer.inxLen = indices.length;
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
return buffer;

VertexBuffer.Draw = function (bufObj)
for (var i = 0; i < bufObj.buf.length; ++i)
gl.bindBuffer(gl.ARRAY_BUFFER, bufObj.buf[i]);
gl.vertexAttribPointer(bufObj.attr[i].loc, bufObj.attr[i].size, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(bufObj.attr[i].loc);

gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, bufObj.inx);
gl.drawElements(gl.TRIANGLES, bufObj.inxLen, gl.UNSIGNED_SHORT, 0);
for (var i = 0; i < bufObj.buf.length; ++i)
gl.disableVertexAttribArray(bufObj.attr[i].loc);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);


initScene();

)();


html, body margin: 0; overflow: hidden;
#gui position: absolute; top: 0; left: 0;


<script id="draw-shader-vs" type="x-shader/x-vertex">
precision highp float;

attribute vec3 inPos;
attribute vec3 inNV;
attribute vec2 inUV;
attribute vec3 inCol;

varying vec3 v_view_pos;
varying vec3 v_view_nv;
varying vec2 v_uv;
varying vec3 v_col;
varying float clip_distance;

uniform mat4 u_projectionMat44;
uniform mat4 u_viewMat44;
uniform mat4 u_modelMat44;
uniform vec4 u_clipPlane;

void main()

mat4 mv = u_viewMat44 * u_modelMat44;
v_col = inCol;
v_uv = inUV;
v_view_nv = normalize(mat3(mv) * inNV);
vec4 viewPos = mv * vec4( inPos, 1.0 );
v_view_pos = viewPos.xyz;
gl_Position = u_projectionMat44 * viewPos;
vec4 worldPos = u_modelMat44 * vec4( inPos, 1.0 );
vec4 clipPlane = vec4(normalize(u_clipPlane.xyz), u_clipPlane.w);
clip_distance = dot(worldPos, clipPlane);

</script>

<script id="draw-shader-fs" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;

varying vec3 v_view_pos;
varying vec3 v_view_nv;
varying vec2 v_uv;
varying vec3 v_col;
varying float clip_distance;

uniform sampler2D u_texture;
uniform sampler2D u_displacement_map;
uniform vec2 u_displacement_map_size;
uniform float u_displacement_scale;
uniform vec2 u_parallax_quality;

uniform vec3 u_lightDir;
uniform float u_ambient;
uniform float u_diffuse;
uniform float u_specular;
uniform float u_shininess;

float CalculateHeight( in vec2 texCoords )

float height = texture2D( u_displacement_map, texCoords ).x;
return clamp( height, 0.0, 1.0 );


vec4 CalculateNormal( in vec2 texCoords )

vec2 texOffs = 1.0 / u_displacement_map_size;
vec2 scale = 1.0 / texOffs;

float h_mid = texture2D( u_displacement_map, texCoords.st ).r;
float h_xa = texture2D( u_displacement_map, texCoords.st + texOffs * vec2(-1.0, 0.0) ).r;
float h_xb = texture2D( u_displacement_map, texCoords.st + texOffs * vec2( 1.0, 0.0) ).r;
float h_ya = texture2D( u_displacement_map, texCoords.st + texOffs * vec2( 0.0, -1.0) ).r;
float h_yb = texture2D( u_displacement_map, texCoords.st + texOffs * vec2( 0.0, 1.0) ).r;
vec2 deltaH = vec2(h_xa-h_xb, h_ya-h_yb);

return vec4( normalize( vec3( deltaH * scale, 1.0 ) ), h_mid );


mat3 mat3_inverse( mat3 A )

mat3 M_t = mat3(
vec3( A[0][0], A[1][0], A[2][0] ),
vec3( A[0][1], A[1][1], A[2][1] ),
vec3( A[0][2], A[1][2], A[2][2] ) );
float det = dot( cross( M_t[0], M_t[1] ), M_t[2] );
mat3 adjugate = mat3( cross( M_t[1], M_t[2] ),
cross( M_t[2], M_t[0] ),
cross( M_t[0], M_t[1] ) );
return adjugate / det;


// Parallax Occlusion Mapping in GLSL [http://sunandblackcat.com/tipFullView.php?topicid=28]
vec3 ParallaxOcclusion( in float frontFace, in vec3 texDir3D, in vec2 texCoord )

vec2 quality_range = u_parallax_quality;
float quality = mix( quality_range.x, quality_range.y, 1.0 - pow(abs(normalize(texDir3D).z),2.0) );
float numSteps = clamp( quality * 50.0, 1.0, 50.0 );
int numBinarySteps = int( clamp( quality * 10.0, 1.0, 7.0 ) );

float surf_sign = frontFace;
float back_face = step(0.0, -surf_sign);
vec2 texStep = surf_sign * texDir3D.xy / abs(texDir3D.z); // (z is negative) the direction vector points downwards int tangent-space
vec2 texC = texCoord.st + surf_sign * texStep + back_face * texStep.xy;
float mapHeight = 1.0;
float bestBumpHeight = mapHeight;
float bumpHeightStep = 1.0 / numSteps;

for ( int i = 0; i < 50; ++ i )

if ( i >= int( numSteps ) )
break;
mapHeight = back_face + surf_sign * CalculateHeight( texC.xy - bestBumpHeight * texStep.xy );
if ( mapHeight >= bestBumpHeight )
break;
bestBumpHeight -= bumpHeightStep;

bestBumpHeight += bumpHeightStep;
for ( int i = 0; i < 7; ++ i )

bumpHeightStep *= 0.5;
bestBumpHeight -= bumpHeightStep;
mapHeight = back_face + surf_sign * CalculateHeight( texC.xy - bestBumpHeight * texStep.xy );
bestBumpHeight += ( bestBumpHeight < mapHeight ) ? bumpHeightStep : 0.0;

bestBumpHeight -= bumpHeightStep * clamp( ( bestBumpHeight - mapHeight ) / bumpHeightStep, 0.0, 1.0 );
mapHeight = bestBumpHeight;
texC -= mapHeight * texStep;

return vec3( texC.xy, mapHeight );


void main()

if ( clip_distance < 0.0 )
discard;

vec2 texCoords = v_uv;
float face_sign = sign(dot(v_view_nv, -v_view_pos));

// Followup: Normal Mapping Without Precomputed Tangents [http://www.thetenthplanet.de/archives/1180]
vec3 N = normalize(v_view_nv);
vec3 dp1 = dFdx( v_view_pos );
vec3 dp2 = dFdy( v_view_pos );
vec2 duv1 = dFdx( v_uv );
vec2 duv2 = dFdy( v_uv );
vec3 dp2perp = cross(dp2, N);
vec3 dp1perp = cross(N, dp1);
vec3 T = dp2perp * duv1.x + dp1perp * duv2.x;
vec3 B = dp2perp * duv1.y + dp1perp * duv2.y;
float invmax = inversesqrt(max(dot(T, T), dot(B, B)));
mat3 tbnMat = mat3(T * invmax, B * invmax, N * u_displacement_scale);

vec3 texDir3D = normalize( mat3_inverse( tbnMat ) * v_view_pos );
vec3 newTexCoords = abs(u_displacement_scale) < 0.001 ? vec3(texCoords.st, 0.0) : ParallaxOcclusion( face_sign, texDir3D, texCoords.st );
texCoords.st = newTexCoords.xy;

vec4 normalVec = CalculateNormal( texCoords );
tbnMat[2] = face_sign * N / u_displacement_scale;
vec3 nvMappedEs = normalize( tbnMat * normalVec.xyz );

//vec3 color = v_col;
vec3 color = texture2D( u_texture, texCoords.st ).rgb;

// ambient part
vec3 lightCol = u_ambient * color;

// diffuse part
vec3 normalV = normalize( nvMappedEs );
vec3 lightV = normalize( -u_lightDir );
float NdotL = max( 0.0, dot( normalV, lightV ) );
lightCol += NdotL * u_diffuse * color;

// specular part
vec3 eyeV = normalize( -v_view_pos );
vec3 halfV = normalize( eyeV + lightV );
float NdotH = max( 0.0, dot( normalV, halfV ) );
float kSpecular = ( u_shininess + 2.0 ) * pow( NdotH, u_shininess ) / ( 2.0 * 3.14159265 );
lightCol += kSpecular * u_specular * color;

gl_FragColor = vec4( lightCol.rgb, 1.0 );

</script>

<div>
<form id="gui" name="inputs">
<table>
<tr><td> <font color=#CCF><span id="fps_val">0</span></font> </td></tr>
<tr>
<td> <font color=#CCF>clipping</font> </td>
<td> <input type="range" id="clip" min="0" max="100" value="100" onchange="changeEventHandler(event);" /></td>
</tr>
<tr>
<td> <font color=#CCF>dispalcement</font> </td>
<td> <input type="range" id="displacement" min="0" max="100" value="50" onchange="changeEventHandler(event);" /></td>
</tr>
<tr>
<td> <font color=#CCF>quality</font> </td>
<td> <input type="range" id="quality" min="0" max="100" value="50" onchange="changeEventHandler(event);" /></td>
</tr>
</table>
</form>
</div>

<canvas id="canvas" style="border: none;"></canvas>



texture
height map





But how do I do that and where? Your example looks really complex to me...
– Lisalein
Sep 2 at 16:09






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

𛂒𛀶,𛀽𛀑𛂀𛃧𛂓𛀙𛃆𛃑𛃷𛂟𛁡𛀢𛀟𛁤𛂽𛁕𛁪𛂟𛂯,𛁞𛂧𛀴𛁄𛁠𛁼𛂿𛀤 𛂘,𛁺𛂾𛃭𛃭𛃵𛀺,𛂣𛃍𛂖𛃶 𛀸𛃀𛂖𛁶𛁏𛁚 𛂢𛂞 𛁰𛂆𛀔,𛁸𛀽𛁓𛃋𛂇𛃧𛀧𛃣𛂐𛃇,𛂂𛃻𛃲𛁬𛃞𛀧𛃃𛀅 𛂭𛁠𛁡𛃇𛀷𛃓𛁥,𛁙𛁘𛁞𛃸𛁸𛃣𛁜,𛂛,𛃿,𛁯𛂘𛂌𛃛𛁱𛃌𛂈𛂇 𛁊𛃲,𛀕𛃴𛀜 𛀶𛂆𛀶𛃟𛂉𛀣,𛂐𛁞𛁾 𛁷𛂑𛁳𛂯𛀬𛃅,𛃶𛁼

Edmonton

Crossroads (UK TV series)