export const vs_normalMapping =
`#version 300 es
precision mediump float;
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;
layout (location = 3) in vec3 aTangent;
layout (location = 4) in vec3 aBitangent;
out vec3 FragPos;
out vec2 TexCoords;
out vec3 TangentLightPos;
out vec3 TangentViewPos;
out vec3 TangentFragPos;
uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;
uniform vec3 lightPos;
uniform vec3 viewPos;
void main()
{
FragPos = vec3(model * vec4(aPos, 1.0));
TexCoords = aTexCoords;
mat3 normalMatrix = transpose(inverse(mat3(model)));
vec3 T = normalize(normalMatrix * aTangent);
vec3 N = normalize(normalMatrix * aNormal);
T = normalize(T - dot(T, N) * N);
vec3 B = cross(N, T);
mat3 TBN = transpose(mat3(T, B, N));
TangentLightPos = TBN * lightPos;
TangentViewPos = TBN * viewPos;
TangentFragPos = TBN * FragPos;
gl_Position = projection * view * model * vec4(aPos, 1.0);
}`
export const fs_normalMapping =
`#version 300 es
precision mediump float;
out vec4 FragColor;
in vec3 FragPos;
in vec2 TexCoords;
in vec3 TangentLightPos;
in vec3 TangentViewPos;
in vec3 TangentFragPos;
uniform sampler2D diffuseMap;
uniform sampler2D normalMap;
uniform vec3 lightPos;
uniform vec3 viewPos;
void main()
{
// obtain normal from normal map in range [0,1]
vec3 normal = texture(normalMap, TexCoords).rgb;
// transform normal vector to range [-1,1]
normal = normalize(normal * 2.0 - 1.0); // this normal is in tangent space
// get diffuse color
vec3 color = texture(diffuseMap, TexCoords).rgb;
// ambient
vec3 ambient = 0.1 * color;
// diffuse
vec3 lightDir = normalize(TangentLightPos - TangentFragPos);
float diff = max(dot(lightDir, normal), 0.0);
vec3 diffuse = diff * color;
// specular
vec3 viewDir = normalize(TangentViewPos - TangentFragPos);
vec3 reflectDir = reflect(-lightDir, normal);
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, halfwayDir), 0.0), 32.0);
vec3 specular = vec3(0.2) * spec;
FragColor = vec4(ambient + diffuse + specular, 1.0);
}`
gl.enable(gl.DEPTH_TEST);
shader = new Shader(gl, vs_normalMapping, fs_normalMapping);
diffuseMap = loadTexture("../../textures/brickwall.jpg", 4, false);
normalMap = loadTexture("../../textures/brickwall_normal.jpg", 4, false);
shader.use(gl);
shader.setInt(gl, "diffuseMap", 0);
shader.setInt(gl, "normalMap", 1);
lightPos = vec3.fromValues(0.5, 1.0, 0.3);
function loadTexture(url, nrComponents, gammaCorrection) {
const textureID = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, textureID);
const level = 0;
const srcType = gl.UNSIGNED_BYTE;
const pixel = new Uint8Array([0, 0, 255, 255]);
gl.texImage2D(gl.TEXTURE_2D, level, gl.RGBA, 1, 1, 0, gl.RGBA, srcType, pixel);
const image = new Image();
image.onload = function () {
let internalFormat;
let dataFormat;
if (nrComponents == 1) {
internalFormat = dataFormat = gl.RED;
}
else if (nrComponents == 3) {
internalFormat = gammaCorrection ? gl.SRGB : gl.RGB;
dataFormat = gl.RGB;
}
else if (nrComponents == 4) {
internalFormat = gammaCorrection ? gl.SRGB8_ALPHA8 : gl.RGBA8;
dataFormat = gl.RGBA;
}
gl.bindTexture(gl.TEXTURE_2D, textureID);
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, image.naturalWidth, image.naturalHeight, 0, dataFormat, gl.UNSIGNED_BYTE, image);
gl.generateMipmap(gl.TEXTURE_2D);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
};
image.src = url;
return textureID;
}
let shader = null
let glPlaneVAO = 0
let diffuseMap, normalMap
let projection = mat4.create(), view = mat4.create()
let lightPos
let quadVAO = null
let camera = new Camera(vec3.fromValues(0.0, 0.0, 5.0), vec3.fromValues(0.0, 1.0, 0.0))
function render() {
let currentFrame = performance.now()
deltaTime = currentFrame - lastFrame
lastFrame = currentFrame
processInput()
gl.clearColor(0.1, 0.1, 0.1, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
mat4.perspective(projection, (camera.Zoom) * Math.PI / 180, canvas.width / canvas.height, 0.1, 100.0)
view = camera.GetViewMatrix()
shader.use(gl)
gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "projection"), false, projection)
gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "view"), false, view)
let model = mat4.create()
let axis = vec3.create()
vec3.normalize(axis, vec3.fromValues(1.0, 0.0, 1.0))
mat4.rotate(model, model, (currentFrame / 1000.0) * Math.PI / 180, axis)
gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "model"), false, model)
gl.uniform3fv(gl.getUniformLocation(shader.programId, "viewPos"), camera.Position)
gl.uniform3fv(gl.getUniformLocation(shader.programId, "lightPos"), lightPos)
gl.activeTexture(gl.TEXTURE0)
gl.bindTexture(gl.TEXTURE_2D, diffuseMap)
gl.activeTexture(gl.TEXTURE1)
gl.bindTexture(gl.TEXTURE_2D, normalMap)
renderQuad()
mat4.identity(model)
mat4.translate(model, model, lightPos)
mat4.scale(model, model, vec3.fromValues(0.1, 0.1, 0.1))
gl.uniformMatrix4fv(gl.getUniformLocation(shader.programId, "model"), false, model)
renderQuad()
}
function renderQuad() {
let quadVBO = null;
if (!quadVAO) {
let pos1 = vec3.fromValues(-1.0, 1.0, 0.0);
let pos2 = vec3.fromValues(-1.0, -1.0, 0.0);
let pos3 = vec3.fromValues(1.0, -1.0, 0.0);
let pos4 = vec3.fromValues(1.0, 1.0, 0.0);
let uv1 = vec2.fromValues(0.0, 1.0);
let uv2 = vec2.fromValues(0.0, 0.0);
let uv3 = vec2.fromValues(1.0, 0.0);
let uv4 = vec2.fromValues(1.0, 1.0);
let nm = vec3.fromValues(0.0, 0.0, 1.0);
let tangent1 = vec3.create(), bitangent1 = vec3.create();
let tangent2 = vec3.create(), bitangent2 = vec3.create();
let edge1 = vec3.create();
vec3.subtract(edge1, pos2, pos1);
let edge2 = vec3.create();
vec3.subtract(edge2, pos3, pos1);
let deltaUV1 = vec2.create();
vec2.subtract(deltaUV1, uv2, uv1);
let deltaUV2 = vec2.create();
vec2.subtract(deltaUV2, uv3, uv1);
let f = 1.0 / (deltaUV1[0] * deltaUV2[1] - deltaUV2[0] * deltaUV1[1]);
tangent1[0] = f * (deltaUV2[1] * edge1[0] - deltaUV1[1] * edge2[0]);
tangent1[1] = f * (deltaUV2[1] * edge1[1] - deltaUV1[1] * edge2[1]);
tangent1[2] = f * (deltaUV2[1] * edge1[2] - deltaUV1[1] * edge2[2]);
vec3.normalize(tangent1, tangent1);
bitangent1[0] = f * (-deltaUV2[0] * edge1[0] + deltaUV1[0] * edge2[0]);
bitangent1[1] = f * (-deltaUV2[0] * edge1[1] + deltaUV1[0] * edge2[1]);
bitangent1[2] = f * (-deltaUV2[0] * edge1[2] + deltaUV1[0] * edge2[2]);
vec3.normalize(bitangent1, bitangent1);
vec3.subtract(edge1, pos3, pos1);
vec3.subtract(edge2, pos4, pos1);
vec2.subtract(deltaUV1, uv3, uv1);
vec2.subtract(deltaUV2, uv4, uv1);
f = 1.0 / (deltaUV1[0] * deltaUV2[1] - deltaUV2[0] * deltaUV1[1]);
tangent2[0] = f * (deltaUV2[1] * edge1[0] - deltaUV1[1] * edge2[0]);
tangent2[1] = f * (deltaUV2[1] * edge1[1] - deltaUV1[1] * edge2[1]);
tangent2[2] = f * (deltaUV2[1] * edge1[2] - deltaUV1[1] * edge2[2]);
vec3.normalize(tangent2, tangent2);
bitangent2[0] = f * (-deltaUV2[0] * edge1[0] + deltaUV1[0] * edge2[0]);
bitangent2[1] = f * (-deltaUV2[0] * edge1[1] + deltaUV1[0] * edge2[1]);
bitangent2[2] = f * (-deltaUV2[0] * edge1[2] + deltaUV1[0] * edge2[2]);
vec3.normalize(bitangent2, bitangent2);
let quadVertices = new Float32Array([
pos1[0], pos1[1], pos1[2], nm[0], nm[1], nm[2], uv1[0], uv1[1], tangent1[0], tangent1[1], tangent1[2], bitangent1[0], bitangent1[1], bitangent1[2],
pos2[0], pos2[1], pos2[2], nm[0], nm[1], nm[2], uv2[0], uv2[1], tangent1[0], tangent1[1], tangent1[2], bitangent1[0], bitangent1[1], bitangent1[2],
pos3[0], pos3[1], pos3[2], nm[0], nm[1], nm[2], uv3[0], uv3[1], tangent1[0], tangent1[1], tangent1[2], bitangent1[0], bitangent1[1], bitangent1[2],
pos1[0], pos1[1], pos1[2], nm[0], nm[1], nm[2], uv1[0], uv1[1], tangent2[0], tangent2[1], tangent2[2], bitangent2[0], bitangent2[1], bitangent2[2],
pos3[0], pos3[1], pos3[2], nm[0], nm[1], nm[2], uv3[0], uv3[1], tangent2[0], tangent2[1], tangent2[2], bitangent2[0], bitangent2[1], bitangent2[2],
pos4[0], pos4[1], pos4[2], nm[0], nm[1], nm[2], uv4[0], uv4[1], tangent2[0], tangent2[1], tangent2[2], bitangent2[0], bitangent2[1], bitangent2[2]
]);
quadVAO = gl.createVertexArray();
quadVBO = gl.createBuffer();
gl.bindVertexArray(quadVAO);
gl.bindBuffer(gl.ARRAY_BUFFER, quadVBO);
gl.bufferData(gl.ARRAY_BUFFER, quadVertices, gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, 14 * sizeFloat, 0);
gl.enableVertexAttribArray(1);
gl.vertexAttribPointer(1, 3, gl.FLOAT, false, 14 * sizeFloat, 3 * sizeFloat);
gl.enableVertexAttribArray(2);
gl.vertexAttribPointer(2, 2, gl.FLOAT, false, 14 * sizeFloat, 6 * sizeFloat);
gl.enableVertexAttribArray(3);
gl.vertexAttribPointer(3, 3, gl.FLOAT, false, 14 * sizeFloat, 8 * sizeFloat);
gl.enableVertexAttribArray(4);
gl.vertexAttribPointer(4, 3, gl.FLOAT, false, 14 * sizeFloat, 11 * sizeFloat);
}
gl.bindVertexArray(quadVAO);
gl.drawArrays(gl.TRIANGLES, 0, 6);
gl.bindVertexArray(null);
}