This JavaScript program demonstrates how to rotate a texture around the y-axis in WebGL.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>XoaX.net's WebGL</title>
<style>
#idCanvas {
border: 1px solid black;
width: 800px;
height: 600px
}
</style>
<script id="idVertexShader" type="c">
attribute vec4 av4Vertex;
attribute vec2 av2UV;
varying vec2 vv2FragUV;
uniform mat4 um4Model;
uniform mat4 um4View;
uniform mat4 um4Projection;
void main() {
gl_Position = um4Projection*um4View*um4Model*av4Vertex;
vv2FragUV = av2UV;
}
</script>
<script id="idFragmantShader" type="c">
precision mediump float;
varying vec2 vv2FragUV;
uniform sampler2D us2Texture;
void main() {
gl_FragColor = texture2D(us2Texture, vv2FragUV);
}
</script>
<script type="text/javascript" src="RotateTextureAroundY.js"></script>
</head>
<body onload="Render()">
<canvas id="idCanvas"></canvas>
</body>
</html>var gqScene = null;
var gdTime = 0.0;
function Render() {
if (gqScene == null) {
gqScene = new CScene();
}
let dLastTime = gdTime;
let qDate = new Date();
// Millseconds since January 1, 1970
gdTime = qDate.getTime()/6000.0;
let dDeltaTime = gdTime - dLastTime;
RotateY(gqScene.mfaModel, 2*Math.PI*dDeltaTime);
const kqGL = gqScene.mqGL;
const kqShaderProgram = gqScene.mqShaderProgram;
const kqPositionBuffer = gqScene.mqPositionBuffer;
const kqTextureBuffer = gqScene.mqTextureBuffer;
kqGL.bindBuffer(kqGL.ARRAY_BUFFER, kqPositionBuffer);
kqGL.vertexAttribPointer(kqGL.getAttribLocation(kqShaderProgram, "av4Vertex"), 3, kqGL.FLOAT, true, 0, 0);
kqGL.enableVertexAttribArray(kqGL.getAttribLocation(kqShaderProgram, "av4Vertex"));
kqGL.bindBuffer(kqGL.ARRAY_BUFFER, kqTextureBuffer);
kqGL.vertexAttribPointer(kqGL.getAttribLocation(kqShaderProgram, "av2UV"), 2, kqGL.FLOAT, true, 0, 0);
kqGL.enableVertexAttribArray(kqGL.getAttribLocation(kqShaderProgram, "av2UV"));
kqGL.useProgram(kqShaderProgram);
kqGL.uniformMatrix4fv(kqGL.getUniformLocation(kqShaderProgram, "um4Model"), false, gqScene.mfaModel);
kqGL.uniformMatrix4fv(kqGL.getUniformLocation(kqShaderProgram, "um4View"), false, gqScene.mfaView);
kqGL.uniformMatrix4fv(kqGL.getUniformLocation(kqShaderProgram, "um4Projection"), false, gqScene.mfaProjection);
kqGL.activeTexture(kqGL.TEXTURE0);
kqGL.bindTexture(kqGL.TEXTURE_2D, gqScene.miTextureID);
kqGL.uniform1i(kqGL.getUniformLocation(kqShaderProgram, "us2Texture"), 0);
kqGL.drawElements(kqGL.TRIANGLES, 6, kqGL.UNSIGNED_SHORT, 0);
requestAnimationFrame(Render);
}
class CScene {
constructor() {
const qCanvas = document.getElementById("idCanvas");
qCanvas.width = qCanvas.clientWidth;
qCanvas.height = qCanvas.clientHeight;
const kqGL = qCanvas.getContext("webgl");
this.mqGL = kqGL;
// Compile the vertex shader
const ksVertexShaderCode = document.getElementById("idVertexShader").innerHTML;
let qVertexShader = kqGL.createShader(kqGL.VERTEX_SHADER);
kqGL.shaderSource(qVertexShader, ksVertexShaderCode);
kqGL.compileShader(qVertexShader);
// Compile the fragment shader
const ksFragmentShaderCode = document.getElementById("idFragmantShader").innerHTML;
let qFragmentShader = kqGL.createShader(kqGL.FRAGMENT_SHADER);
kqGL.shaderSource(qFragmentShader, ksFragmentShaderCode);
kqGL.compileShader(qFragmentShader);
// Compile and link the program
let qProgram = kqGL.createProgram();
kqGL.attachShader(qProgram, qVertexShader);
kqGL.attachShader(qProgram, qFragmentShader);
kqGL.linkProgram(qProgram);
//kqGL.useProgram(qProgram);
this.mqShaderProgram = qProgram;
if (!kqGL.getProgramParameter(qProgram, kqGL.LINK_STATUS)) {
alert('The program failed to initialize', kqGL.getProgramInfoLog(qProgram));
}
// Create the buffers
this.mfaPositions = [-1.0, 0.0, -1.0, 1.0, 0.0, -1.0, 1.0, 0.0, 1.0, -1.0, 0.0, 1.0];
let qPositionBuffer = kqGL.createBuffer();
kqGL.bindBuffer(kqGL.ARRAY_BUFFER, qPositionBuffer);
kqGL.bufferData(kqGL.ARRAY_BUFFER, new Float32Array(this.mfaPositions), kqGL.STATIC_DRAW);
this.mqPositionBuffer = qPositionBuffer;
this.miaIndices = [0, 2, 1, 0, 3, 2];
let qIndexBuffer = kqGL.createBuffer();
kqGL.bindBuffer(kqGL.ELEMENT_ARRAY_BUFFER, qIndexBuffer);
kqGL.bufferData(kqGL.ELEMENT_ARRAY_BUFFER, new Uint16Array(this.miaIndices), kqGL.STATIC_DRAW);
this.mfaTextureCoordinates = [0.25, 0.25, 0.75, 0.25, 0.75, 0.75, 0.25, 0.75]; //[0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0];
let qTextureBuffer = kqGL.createBuffer();
kqGL.bindBuffer(kqGL.ARRAY_BUFFER, qTextureBuffer);
kqGL.bufferData(kqGL.ARRAY_BUFFER, new Float32Array(this.mfaTextureCoordinates), kqGL.STATIC_DRAW);
this.mqTextureBuffer = qTextureBuffer;
// Create the texture
this.miTextureSize = 2;
this.mfaTexture = new Uint8Array(4*this.miTextureSize*this.miTextureSize)
const kiMax = (this.miTextureSize - 1)*(this.miTextureSize - 1);
for (let i = 0; i <= this.miTextureSize; ++i) {
for (let j = 0; j <= this.miTextureSize; ++j) {
// Red, Green, Blue, Alpha
this.mfaTexture[4*(this.miTextureSize*i + j)] = Math.round(Math.random()*255);
this.mfaTexture[4*(this.miTextureSize*i + j) + 1] = Math.round(Math.random()*255);
this.mfaTexture[4*(this.miTextureSize*i + j) + 2] = Math.round(Math.random()*255);
this.mfaTexture[4*(this.miTextureSize*i + j) + 3] = 255;
this.mfaTexture[4*(this.miTextureSize*i + j)] = ((i+j) % 2)*255;
this.mfaTexture[4*(this.miTextureSize*i + j) + 1] = 0;
this.mfaTexture[4*(this.miTextureSize*i + j) + 2] = 0;
this.mfaTexture[4*(this.miTextureSize*i + j) + 3] = 255;
}
}
this.miTextureID = kqGL.createTexture();
kqGL.bindTexture(kqGL.TEXTURE_2D, this.miTextureID);
kqGL.texImage2D(kqGL.TEXTURE_2D, 0, kqGL.RGBA, this.miTextureSize, this.miTextureSize, 0,
kqGL.RGBA, kqGL.UNSIGNED_BYTE, this.mfaTexture);
kqGL.texParameteri(kqGL.TEXTURE_2D, kqGL.TEXTURE_WRAP_S, kqGL.CLAMP_TO_EDGE);
kqGL.texParameteri(kqGL.TEXTURE_2D, kqGL.TEXTURE_WRAP_T, kqGL.CLAMP_TO_EDGE);
kqGL.texParameteri(kqGL.TEXTURE_2D, kqGL.TEXTURE_MIN_FILTER, kqGL.NEAREST);
// Create the matrices
this.mfaModel = Identity();
Scale(this.mfaModel, [1, 0, 1]);
const kdFOV = Math.PI*(45.0/180.0);
const kdAspect = qCanvas.clientWidth/qCanvas.clientHeight;
const kdNearPlane = 0.1;
const kdFarPlane = 1000.0;
this.mfaProjection = Perspective(kdFOV, kdAspect, kdNearPlane, kdFarPlane);
this.mfaView = Identity();
// Create the view matrix
let daEye = [-3.0, 2.0, 0.0];
let daCenter = [0.0, 0.0, 0.0];
let daUp = [0.0, 1.0, 0.0];
LookAt(this.mfaView, daEye, daCenter, daUp);
}
}
function Identity() {
let faI = new Float32Array(16);
faI[0] = 1.0; faI[1] = 0.0; faI[2] = 0.0; faI[3] = 0.0;
faI[4] = 0.0; faI[5] = 1.0; faI[6] = 0.0; faI[7] = 0.0;
faI[8] = 0.0; faI[9] = 0.0; faI[10] = 1.0; faI[11] = 0.0;
faI[12] = 0.0; faI[13] = 0.0; faI[14] = 0.0; faI[15] = 1.0;
return faI;
}
function Scale(faM, faScale) {
for (let i = 0; i < 3; ++i) {
for (let j = 0; j < 4; ++j) {
faM[j + i*4] *= faScale[i];
}
}
}
function RotateY(faM, fAngleRadians) {
let dSin = Math.sin(fAngleRadians);
let dCos = Math.cos(fAngleRadians);
// Temp storage
let faV = new Float32Array(8);
// Perform the rotation to get the resulting x and z rows
for (let i = 0; i < 4; ++i) {
faV[i] = dCos*faM[i] + dSin*faM[i+8];
faV[i + 4] = -dSin*faM[i] + dCos*faM[i+8];
}
// Copy the rotated rows back to the matrix
for (let i = 0; i < 4; ++i) {
faM[i] = faV[i];
faM[i + 8] = faV[i+4];
}
}
function Perspective(dFOV, dAspect, dNear, dFar) {
let faM = new Float32Array(16);
let dF = 1.0/Math.tan(dFOV/2.0);
let dNF = 1/(dNear-dFar);
faM[0] = dF/dAspect; faM[1] = 0.0; faM[2] = 0.0; faM[3] = 0.0;
faM[4] = 0.0; faM[5] = dF; faM[6] = 0.0; faM[7] = 0.0;
faM[8] = 0.0; faM[9] = 0.0; faM[10] = (dNear+dFar)*dNF; faM[11] = -1.0;
faM[12] = 0.0; faM[13] = 0.0; faM[14] = 2.0*dNear*dFar*dNF; faM[15] = 0.0;
return faM;
}
function LookAt(faM, faEye, faCenter, faUp) {
let faView = Difference(faEye, faCenter);
Normalize(faView);
let faX = Cross(faUp, faView);
Normalize(faX);
let faY = Cross(faView, faX);
Normalize(faY);
faM[0] = faX[0]; faM[1] = faY[0]; faM[2] = faView[0]; faM[3] = 0.0;
faM[4] = faX[1]; faM[5] = faY[1]; faM[6] = faView[1]; faM[7] = 0.0;
faM[8] = faX[2]; faM[9] = faY[2]; faM[10] = faView[2]; faM[11] = 0.0;
faM[12] = -Dot(faX, faEye)
faM[13] = -Dot(faY, faEye)
faM[14] = -Dot(faView, faEye)
faM[15] = 1.0;
}
function Dot(faV1, faV2) {
let dDot = 0.0;
for (let i = 0; i < 3; ++i) {
dDot += faV1[i]*faV2[i];
}
return dDot;
}
function Difference(faV1, faV2) {
let faDiff = new Float32Array(3);
for (let i = 0; i < 3; ++i) {
faDiff[i] = faV1[i] - faV2[i];
}
return faDiff;
}
function Cross(faV1, faV2) {
let faCross = new Float32Array(3);
for (let i = 0; i < 3; ++i) {
faCross[i] = faV1[(i+1)%3]*faV2[(i+2)%3] - faV1[(i+2)%3]*faV2[(i+1)%3];
}
return faCross;
}
function Normalize(faV) {
let dLength = 1.0/Math.sqrt(faV[0]*faV[0] + faV[1]*faV[1] + faV[2]*faV[2]);
for (let i = 0; i < 3; ++i) {
faV[i] *= dLength;
}
}© 20072026 XoaX.net LLC. All rights reserved.