texture support

This commit is contained in:
gbrochar 2020-11-24 19:18:45 +01:00
parent b1168ca920
commit ce7914732e
9 changed files with 198 additions and 90 deletions

BIN
public/textures/fox.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
public/textures/ice.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
public/textures/noise.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
public/textures/wall.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 718 KiB

View File

@ -5,13 +5,15 @@
* @param {Array<number>} positions the position buffer to be loaded * @param {Array<number>} positions the position buffer to be loaded
* @param {Array<number>} indices the index buffer to be loaded * @param {Array<number>} indices the index buffer to be loaded
* @param {Array<number>} normals the normal buffer to be loaded * @param {Array<number>} normals the normal buffer to be loaded
* @param {Array<number>} uvs the texture buffer to be loaded
* @return {any} the buffers * @return {any} the buffers
*/ */
export function initBuffers( export function initBuffers(
gl: any, gl: any,
positions: Array<number>, positions: Array<number>,
indices: Array<number>, indices: Array<number>,
normals: Array<number>) { normals: Array<number>,
uvs: Array<number>) {
const positionBuffer = gl.createBuffer(); const positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer); gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData( gl.bufferData(
@ -32,10 +34,17 @@ export function initBuffers(
new Uint16Array(indices), gl.STATIC_DRAW); new Uint16Array(indices), gl.STATIC_DRAW);
// Now send the element array to GL // Now send the element array to GL
const uvBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, uvBuffer);
gl.bufferData(gl.ARRAY_BUFFER,
new Float32Array(uvs),
gl.STATIC_DRAW);
return { return {
position: positionBuffer, positions: positionBuffer,
indices: indexBuffer, indices: indexBuffer,
normals: normalBuffer, normals: normalBuffer,
uvs: uvBuffer,
}; };
} }
@ -45,8 +54,8 @@ export function initBuffers(
* @param {any} buffers the buffers to delete * @param {any} buffers the buffers to delete
*/ */
export function deleteBuffers(gl: any, buffers: any) { export function deleteBuffers(gl: any, buffers: any) {
gl.deleteBuffer(buffers.color); gl.deleteBuffer(buffers.uvs);
gl.deleteBuffer(buffers.position); gl.deleteBuffer(buffers.positions);
gl.deleteBuffer(buffers.normals); gl.deleteBuffer(buffers.normals);
gl.deleteBuffer(buffers.indices); gl.deleteBuffer(buffers.indices);
} }

View File

@ -30,6 +30,8 @@ export function changeFragmentShader(gl: any,
'aVertexColor'), 'aVertexColor'),
vertexNormal: gl.getAttribLocation(shaderProgram, vertexNormal: gl.getAttribLocation(shaderProgram,
'aVertexNormal'), 'aVertexNormal'),
textureCoord: gl.getAttribLocation(shaderProgram,
'aTextureCoord'),
}, },
uniformLocations: { uniformLocations: {
projectionMatrix: gl.getUniformLocation( projectionMatrix: gl.getUniformLocation(
@ -38,6 +40,8 @@ export function changeFragmentShader(gl: any,
shaderProgram, 'uviewMatrix'), shaderProgram, 'uviewMatrix'),
modelMatrix: gl.getUniformLocation( modelMatrix: gl.getUniformLocation(
shaderProgram, 'umodelMatrix'), shaderProgram, 'umodelMatrix'),
uSampler: gl.getUniformLocation(
shaderProgram, 'uSampler'),
}, },
}; };
return [programInfo, fragmentShader]; return [programInfo, fragmentShader];

View File

@ -1,12 +1,13 @@
// @ts-ignore // @ts-ignore
import mat4 from 'gl-mat4'; import mat4 from 'gl-mat4';
// @ts-ignore // @ts-ignore
import convert from './objparser'; import {convert} from './objparser';
import $ from 'jquery'; import $ from 'jquery';
import {initBuffers, deleteBuffers} from './buffers'; import {initBuffers, deleteBuffers} from './buffers';
import {initShaderProgram} from './shaders'; import {initShaderProgram} from './shaders';
import {changeFragmentShader} from './changeshader'; import {changeFragmentShader} from './changeshader';
import {loadTexture} from './texture';
let squareRotation = 0.0; let squareRotation = 0.0;
@ -31,6 +32,7 @@ async function main() {
const vsSource = ` const vsSource = `
attribute vec4 aVertexPosition; attribute vec4 aVertexPosition;
attribute vec4 aVertexNormal; attribute vec4 aVertexNormal;
attribute vec2 aTextureCoord;
uniform mat4 uProjectionMatrix; uniform mat4 uProjectionMatrix;
uniform mat4 uviewMatrix; uniform mat4 uviewMatrix;
@ -38,6 +40,8 @@ async function main() {
varying highp vec4 vNormal; varying highp vec4 vNormal;
varying highp vec3 vPosition; varying highp vec3 vPosition;
varying highp vec2 vTextureCoord;
void main() void main()
{ {
gl_Position = uProjectionMatrix * gl_Position = uProjectionMatrix *
@ -46,6 +50,7 @@ async function main() {
aVertexPosition; aVertexPosition;
vPosition = vec3(aVertexPosition); vPosition = vec3(aVertexPosition);
vNormal = umodelMatrix * aVertexNormal; vNormal = umodelMatrix * aVertexNormal;
vTextureCoord = aTextureCoord;
} }
`; `;
@ -151,6 +156,27 @@ async function main() {
vec3 texture = extremize(mod(vPosition.xyz + vec3(1000.), vec3(2.)), 2.) / vec3(2); vec3 texture = extremize(mod(vPosition.xyz + vec3(1000.), vec3(2.)), 2.) / vec3(2);
gl_FragColor = vec4((diffuse * 0.8) + (vec3(0.2)) + (specular * vec3(1.)), 1.0); gl_FragColor = vec4((diffuse * 0.8) + (vec3(0.2)) + (specular * vec3(1.)), 1.0);
}`; }`;
const fsSource4 = `
precision highp float;
varying highp vec2 vTextureCoord;
varying highp vec4 vNormal;
varying highp vec3 vPosition;
uniform sampler2D uSampler;
void main() {
highp vec4 texelColor = texture2D(uSampler, vTextureCoord);
vec3 n = normalize(vec3(-50000., 100000., 50000.) - vPosition);
float diffuse = max(dot(normalize(vNormal.xyz), n), 0.);
float specular = pow(
max(dot(
reflect(n, normalize(vNormal.xyz)),
normalize(vec3(0., 0., -50.) - vPosition)),
0.), 10.);
gl_FragColor = vec4((texelColor.xyz * diffuse * 0.8) + (texelColor.xyz * vec3(0.2)) + (specular * vec3(1.)), 1.0);
}`;
/* eslint-enable */ /* eslint-enable */
@ -165,7 +191,7 @@ async function main() {
return data; return data;
} }
const data = await getObj('/static/objs/teapot.obj'); const data = await getObj('/static/objs/fox.obj');
let distance: any = $('#input1').val(); let distance: any = $('#input1').val();
let circleSize: any = $('#input2').val(); let circleSize: any = $('#input2').val();
distance = parseFloat(distance); distance = parseFloat(distance);
@ -177,10 +203,9 @@ async function main() {
indices, indices,
] = convert(data); ] = convert(data);
let length = indices.length; let length = indices.length;
console.log(uvs);
let [shaderProgram, fragmentShader]: any = initShaderProgram(gl, let [shaderProgram, fragmentShader]: any = initShaderProgram(gl,
vsSource, vsSource,
fsSource3); fsSource4);
let programInfo: any = { let programInfo: any = {
program: shaderProgram, program: shaderProgram,
@ -189,6 +214,8 @@ async function main() {
'aVertexPosition'), 'aVertexPosition'),
vertexNormal: gl.getAttribLocation(shaderProgram, vertexNormal: gl.getAttribLocation(shaderProgram,
'aVertexNormal'), 'aVertexNormal'),
textureCoord: gl.getAttribLocation(shaderProgram,
'aTextureCoord'),
}, },
uniformLocations: { uniformLocations: {
projectionMatrix: gl.getUniformLocation( projectionMatrix: gl.getUniformLocation(
@ -197,10 +224,13 @@ async function main() {
shaderProgram, 'uviewMatrix'), shaderProgram, 'uviewMatrix'),
modelMatrix: gl.getUniformLocation( modelMatrix: gl.getUniformLocation(
shaderProgram, 'umodelMatrix'), shaderProgram, 'umodelMatrix'),
uSampler: gl.getUniformLocation(
shaderProgram, 'uSampler'),
}, },
}; };
let buffers = initBuffers(gl, positions, indices, normals); let texture = loadTexture(gl, '/static/textures/fox.png');
let buffers = initBuffers(gl, positions, indices, normals, uvs);
let then = 0; let then = 0;
let changed = false; let changed = false;
@ -221,7 +251,8 @@ async function main() {
deltaTime, deltaTime,
length, length,
distance, distance,
circleSize); circleSize,
texture);
requestAnimationFrame(render); requestAnimationFrame(render);
} }
@ -236,10 +267,9 @@ async function main() {
uvs, uvs,
indices, indices,
] = convert(data); ] = convert(data);
console.log(uvs);
length = indices.length; length = indices.length;
deleteBuffers(gl, buffers); deleteBuffers(gl, buffers);
buffers = initBuffers(gl, positions, indices, normals); buffers = initBuffers(gl, positions, indices, normals, uvs);
} }
$(function() { $(function() {
@ -255,18 +285,22 @@ async function main() {
$('#changecirclesize').click(); $('#changecirclesize').click();
} }
}); });
$('#button1').on('click', function() { $('#s_blackandwhite').on('click', function() {
[programInfo, fragmentShader] = changeFragmentShader(gl, [programInfo, fragmentShader] = changeFragmentShader(gl,
shaderProgram, fragmentShader, fsSource, vsSource); shaderProgram, fragmentShader, fsSource, vsSource);
}); });
$('#button2').on('click', function() { $('#s_color').on('click', function() {
[programInfo, fragmentShader] = changeFragmentShader(gl, [programInfo, fragmentShader] = changeFragmentShader(gl,
shaderProgram, fragmentShader, fsSource2, vsSource); shaderProgram, fragmentShader, fsSource2, vsSource);
}); });
$('#button3').on('click', function() { $('#s_flat').on('click', function() {
[programInfo, fragmentShader] = changeFragmentShader(gl, [programInfo, fragmentShader] = changeFragmentShader(gl,
shaderProgram, fragmentShader, fsSource3, vsSource); shaderProgram, fragmentShader, fsSource3, vsSource);
}); });
$('#s_texture').on('click', function() {
[programInfo, fragmentShader] = changeFragmentShader(gl,
shaderProgram, fragmentShader, fsSource4, vsSource);
});
$('#changedistance').on('click', function() { $('#changedistance').on('click', function() {
distance = $('#input1').val(); distance = $('#input1').val();
distance = parseFloat(distance); distance = parseFloat(distance);
@ -287,6 +321,18 @@ async function main() {
const data = await getObj('/static/objs/fox.obj'); const data = await getObj('/static/objs/fox.obj');
updateObj(data); updateObj(data);
}); });
$('#t_wall').on('click', async function() {
texture = loadTexture(gl, '/static/textures/wall.png');
});
$('#t_ice').on('click', async function() {
texture = loadTexture(gl, '/static/textures/ice.png');
});
$('#t_noise').on('click', async function() {
texture = loadTexture(gl, '/static/textures/noise.png');
});
$('#t_fox').on('click', async function() {
texture = loadTexture(gl, '/static/textures/fox.png');
});
}); });
requestAnimationFrame(render); requestAnimationFrame(render);
} }
@ -300,6 +346,7 @@ async function main() {
* @param {number} length the index buffer length * @param {number} length the index buffer length
* @param {number} distance distance of camera * @param {number} distance distance of camera
* @param {number} circleSize size of circle the object is rotating around * @param {number} circleSize size of circle the object is rotating around
* @param {any} texture the texture to load
*/ */
function drawScene(gl: any, function drawScene(gl: any,
programInfo: any, programInfo: any,
@ -307,7 +354,8 @@ function drawScene(gl: any,
deltaTime: number, deltaTime: number,
length: number, length: number,
distance: number, distance: number,
circleSize: number) { circleSize: number,
texture: any) {
gl.clearColor(0.0, 0.0, 0.0, 1.0); gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0); gl.clearDepth(1.0);
gl.enable(gl.DEPTH_TEST); gl.enable(gl.DEPTH_TEST);
@ -374,7 +422,7 @@ function drawScene(gl: any,
const normalize = false; const normalize = false;
const stride = 0; const stride = 0;
const offset = 0; const offset = 0;
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.position); gl.bindBuffer(gl.ARRAY_BUFFER, buffers.positions);
gl.vertexAttribPointer( gl.vertexAttribPointer(
programInfo.attribLocations.vertexPosition, programInfo.attribLocations.vertexPosition,
numComponents, numComponents,
@ -405,6 +453,30 @@ function drawScene(gl: any,
programInfo.attribLocations.vertexNormal); programInfo.attribLocations.vertexNormal);
} }
// tell webgl how to pull out the texture coordinates from buffer
{
const num = 2; // every coordinate composed of 2 values
const type = gl.FLOAT; // the data in the buffer is 32 bit float
const normalize = false; // don't normalize
const stride = 0; // how many bytes to get from one set to the next
const offset = 0; // how many bytes inside the buffer to start from
gl.bindBuffer(gl.ARRAY_BUFFER, buffers.uvs);
gl.vertexAttribPointer(programInfo.attribLocations.textureCoord,
num,
type,
normalize,
stride,
offset);
gl.enableVertexAttribArray(programInfo.attribLocations.textureCoord);
}
// Tell WebGL we want to affect texture unit 0
gl.activeTexture(gl.TEXTURE0);
// Bind the texture to texture unit 0
gl.bindTexture(gl.TEXTURE_2D, texture);
// Tell the shader we bound the texture to texture unit 0
gl.uniform1i(programInfo.uniformLocations.uSampler, 0);
// Tell WebGL which indices to use to index the vertices // Tell WebGL which indices to use to index the vertices
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, buffers.indices);

View File

@ -1,6 +1,5 @@
export default function convert (objText) { export function convert (objText) {
const lines = objText.split("\n"); const lines = objText.split("\n");
let smoothing = false; let smoothing = false;
const positions = []; const positions = [];
@ -11,67 +10,87 @@ export default function convert (objText) {
const indices = []; const indices = [];
for (let i = 0; i < lines.length; i++) { for (let i = 0; i < lines.length; i++) {
if (lines[i] != '') const line = lines[i];
{ const rawchunks = line.split(" ").map(x => x.trim());
const rawchunks = lines[i].split(" ").map(x => x.trim()); const chunks = [];
const chunks = []; for (let i = 0; i < rawchunks.length; i++) {
for (let i = 0; i < rawchunks.length; i++) { if (rawchunks[i] != '') {
if (rawchunks[i] != '') { chunks.push(rawchunks[i]);
chunks.push(rawchunks[i]);
}
}
switch (chunks[0]) {
case "v": {
positions.push(chunks.splice(1).map(parseFloat));
break;
}
case "vt": {
const uv = chunks.splice(1);
uvs.push([parseFloat(uv[0]), parseFloat(uv[1])]);
break;
}
case "vn": {
normals.push(chunks.splice(1).map(parseFloat));
break;
}
case "s": {
if (chunks[1] > 0) {
smoothing = true;
} else {
smoothing = false;
}
break;
}
case "f": {
const c1 = (smoothing ? "s" : "") + chunks[1];
const c2 = (smoothing ? "s" : "") + chunks[2];
const c3 = (smoothing ? "s" : "") + chunks[3];
let index1 = vertices.indexOf(c1);
if (index1 === -1) {
index1 = vertices.length;
vertices.push(c1);
}
let index2 = vertices.indexOf(c2);
if (index2 === -1) {
index2 = vertices.length;
vertices.push(c2);
}
let index3 = vertices.indexOf(c3);
if (index3 === -1) {
index3 = vertices.length;
vertices.push(c3);
}
indices.push(index1, index2, index3);
break;
}
} }
} }
switch (chunks[0]) {
case "v": {
positions.push(chunks.splice(1).map(parseFloat));
break;
}
case "vt": {
const uv = chunks.splice(1);
uvs.push([parseFloat(uv[0]), parseFloat(uv[1])]);
break;
}
case "vn": {
normals.push(chunks.splice(1).map(parseFloat));
break;
}
case "s": {
if (chunks[1] > 0) {
smoothing = true;
} else {
smoothing = false;
}
break;
}
case "f": {
const c1 = (smoothing ? "s" : "") + chunks[1];
const c2 = (smoothing ? "s" : "") + chunks[2];
const c3 = (smoothing ? "s" : "") + chunks[3];
let index1 = vertices.indexOf(c1);
if (index1 === -1) {
index1 = vertices.length;
vertices.push(c1);
}
let index2 = vertices.indexOf(c2);
if (index2 === -1) {
index2 = vertices.length;
vertices.push(c2);
}
let index3 = vertices.indexOf(c3);
if (index3 === -1) {
index3 = vertices.length;
vertices.push(c3);
}
indices.push(index1, index2, index3);
break;
}
}
}
const avgNormals = [];
for (let i = 0; i < vertices.length; i++) {
if (vertices[i].startsWith("s")) {
const d = vertices[i].substr(1).split("/");
const normal = normals[d[2] - 1];
const index = d[0] - 1;
if (avgNormals[index]) {
avgNormals[index][0] += normal[0];
avgNormals[index][1] += normal[1];
avgNormals[index][2] += normal[2];
} else {
avgNormals[index] = normal;
}
}
}
for (let n of avgNormals) {
const len = Math.hypot(...n);
avgNormals[avgNormals.indexOf(n)] = n.map(x => x / len);
} }
const outPositions = []; const outPositions = [];
@ -82,12 +101,16 @@ export default function convert (objText) {
const d = (vertices[i].startsWith("s") ? vertices[i].substr(1) : vertices[i]).split("/"); const d = (vertices[i].startsWith("s") ? vertices[i].substr(1) : vertices[i]).split("/");
outPositions.push(...positions[d[0] - 1]); outPositions.push(...positions[d[0] - 1]);
if (normals.length) {
if (vertices[i].startsWith("s")) {
outNormals.push(...avgNormals[d[0] - 1]);
} else {
outNormals.push(...normals[d[2] - 1]); outNormals.push(...normals[d[2] - 1]);
} }
if (uvs.length) { outUVs.push(...uvs[d[1] - 1]);
outUVs.push(...uvs[d[1] - 1]);
}
} }
console.log(indices);
return [outPositions, outNormals, outUVs, indices]; return [outPositions, outNormals, outUVs, indices];
}; };

View File

@ -15,18 +15,18 @@
<canvas id='glCanvas' width='640' height='640'></canvas> <canvas id='glCanvas' width='640' height='640'></canvas>
<div id='ui'> <div id='ui'>
<div class='ui-block'> <div class='ui-block'>
<button id='button1'>Change Shader to black and white</button> <button id='s_blackandwhite'>Change Shader to black and white</button>
<button id='button2'>Change Shader to colored</button> <button id='s_color'>Change Shader to colored</button>
<button id='button3'>Change Shader to flat</button> <button id='s_flat'>Change Shader to flat</button>
<button id='button3'>Change Shader to texture</button> <button id='s_texture'>Change Shader to texture</button>
</div> </div>
<div class='ui-block'> <div class='ui-block'>
<input id='input1' value='50'></input> <input id='input1' value='250'></input>
<button id='changedistance'>Change camera distance</button> <button id='changedistance'>Change camera distance</button>
<div style='display: inline'>Max distance is 1000</div> <div style='display: inline'>Max distance is 1000</div>
</div> </div>
<div class='ui-block'> <div class='ui-block'>
<input id='input2' value='5'></input> <input id='input2' value='30'></input>
<button id='changecirclesize'>Change circle size</button> <button id='changecirclesize'>Change circle size</button>
</div> </div>
<div class='ui-block'> <div class='ui-block'>
@ -35,10 +35,10 @@
<button id='o_fox'>Set object to fox</button> <button id='o_fox'>Set object to fox</button>
</div> </div>
<div class='ui-block'> <div class='ui-block'>
<button id='t_grey'>Set texture to white</button>
<button id='t_wall'>Set texture to wall</button> <button id='t_wall'>Set texture to wall</button>
<button id='t_fox'>Set texture to fox</button>
<button id='t_ice'>Set texture to ice</button> <button id='t_ice'>Set texture to ice</button>
<button id='t_noise'>Set texture to noise</button>
<button id='t_fox'>Set texture to fox</button>
</div> </div>
</div> </div>
</div> </div>