对于独立于荣誉学分的类项目,我需要使用WebGL显示我选择的图像作为纹理使用,然后对纹理进行变形以使其看起来像在以某种方式移动或更改。
为此,我选择对雨滴纹理进行变形以使其看起来像雨滴从屏幕上掉下来。我有一个风景背景纹理(.jpg图像),我想用雨滴纹理(一个.png图像)覆盖它,但是我不确定如何覆盖背景纹理,这样我就可以在同时。我现在拥有的代码仅显示背景图像。
任何人都可以帮助我解释我做错了什么,以及如何解决这个问题?如果您需要我上传部分代码以查看,请告诉我。我几乎不了解WebGL,所以我不知道要显示什么地方才能看到我要去哪里。就我所知,整个事情可能是错误的。
编辑:我被告知要添加代码,因此由于我不知道要显示什么代码段,所以这是我的整个javascript和html文件:
还请注意:此代码仅在FireFox中有效! Google Chrome浏览器会出错! (尽管我认为这会产生错误,因为我没有提供.html文件中引用的文件)*
var canvas;
var gl;
var Index = 0;
var texCoord = [
vec2(0, 0),
vec2(0, 1),
vec2(1, 1),
vec2(1, 1),
vec2(1, 0),
vec2(0, 0)
];
var vertices = [
vec2( -1, -1 ),
vec2( -1, 1 ),
vec2( 1, 1 ),
vec2( 1, 1 ),
vec2( 1, -1 ),
vec2( -1, -1 )
];
var pointsArray = [];
//PROGRAMS
var program1; //for background
var program2; //for rain texture
var program3; //for rain texture updates
//TEXTURES
var texture1; //background image (STATIC IMAGE)
var texture2; // Raindrop texture (bounce between 2 and 3)
var texture3; // Raindrop texture (transform to be sent between 2 and 3)
//BUFFERS--------------------------------------------------------------------
var framebuffer;
var buffer1; //buffer for static background (texture1) vertices
var buffer2; //buffer for static background (texture1) texcoordinates
var buffer3; //buffer for raindrops (texture2) vertices
var buffer4; //buffer for raindrops (texture2) texcoordinates
var buffer5; //buffer for texture3 vertices
var buffer6; //buffer for texture3 texcoordinates
window.onload = function init() {
canvas = document.getElementById( "gl-canvas" );
gl = WebGLUtils.setupWebGL( canvas );
if ( !gl ) { alert( "WebGL isn't available" ); }
//----------------------------------------------------------------------------------------------------------------------------------------
//BACKGROUND texture
texture1 = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture1);
// Fill the texture with a blue screen (fill canvas) FILLER!!!
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2048, 1024, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
// load image
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1);
// Get the IMG tag by its ID
var bgpic = document.getElementById("background");
gl.bindTexture(gl.TEXTURE_2D, texture1);
// Load data from the IMG
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, bgpic);
gl.generateMipmap(gl.TEXTURE_2D);
//------------------------------------------------------------------------------------------------------------------------------------------
//RAINDROP TEXTURE
texture2 = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture2);
// Fill the texture with a blue screen (fill canvas) FILLER!!!
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2048, 1024, 0, gl.RGBA, gl.UNSIGNED_BYTE,
new Uint8Array([0, 0, 255, 255]));
// load image
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1);
// Get the IMG tag by its ID
var rainPic = document.getElementById("raindrop");
gl.bindTexture(gl.TEXTURE_2D, texture2);
// Load data from the IMG
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, rainPic);
gl.generateMipmap(gl.TEXTURE_2D);
//------------------------------------------------------------------------------------------------------------------------------------------
//EMPTY TEXTURE TO PASS TEXTURE2 TO
texture3 = gl.createTexture();
gl.bindTexture( gl.TEXTURE_2D, texture2 );
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2048, 1024, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE );
gl.texParameteri( gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE );
//------------------------------------------------------------------------------------------------------------------------------------------
// Allocate a frame buffer object
framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
framebuffer.width = 4096;
framebuffer.height = 2048;
//------------------------------------------------------------------------------------------------------------------------------------------
// Attach color buffer
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture1, 0);
//------------------------------------------------------------------------------------------------------------------------------------------
// check for completeness
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if(status != gl.FRAMEBUFFER_COMPLETE) alert('Frame Buffer Not Complete');
//------------------------------------------------------------------------------------------------------------------------------------------
//
// Load shaders and initialize attribute buffers
//
program1 = initShaders( gl, "vertex-shader1", "fragment-shader1" );
program2 = initShaders( gl, "vertex-shader2", "fragment-shader2" );
program3 = initShaders(gl, "vertex-shader3","fragment-shader3");
//------------------------------------------------------------------------------------------------------------------------------------------
//PROGRAM 1 CODE
gl.useProgram( program1 );
gl.useProgram(program1);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture1);
// send data to GPU for normal render
buffer1 = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, buffer1);
gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation( program1, "vPosition" );
gl.vertexAttribPointer( vPosition, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vPosition );
buffer2 = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, buffer2);
gl.bufferData( gl.ARRAY_BUFFER, flatten(texCoord), gl.STATIC_DRAW);
var vTexCoord = gl.getAttribLocation( program1, "vTexCoord");
gl.vertexAttribPointer( vTexCoord, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vTexCoord );
gl.uniform1i( gl.getUniformLocation(program1, "texture1"), 0);
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
gl.viewport(0, 0, 2048, 1024);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
//------------------------------------------------------------------------------------------------------------------------------------------
//PROGRAM 2 CODE
gl.useProgram(program2);
// Create and initialize a buffer object
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture2);
buffer3 = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, buffer3 );
gl.bufferData( gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW );
var vPosition = gl.getAttribLocation( program2, "vPosition" );
gl.vertexAttribPointer( vPosition, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vPosition );
// Bind FBO and render
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
gl.viewport(0, 0, 2048, 1024);
gl.clearColor(1.0, 1.0, 1.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT );
// gl.drawArrays(gl.TRIANGLES, 0, numVertices);
// Bind to window system frame buffer, unbind the texture
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
//------------------------------------------------------------------------------------------------------------------------------------------
//PROGRAM 3 CODE
gl.useProgram(program3);
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture3);
// send data to GPU for normal render
buffer5 = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, buffer5);
gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices), gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation( program3, "vPosition" );
gl.vertexAttribPointer( vPosition, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vPosition );
buffer6 = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, buffer6);
gl.bufferData( gl.ARRAY_BUFFER, flatten(texCoord), gl.STATIC_DRAW);
var vTexCoord = gl.getAttribLocation( program3, "vTexCoord");
gl.vertexAttribPointer( vTexCoord, 2, gl.FLOAT, false, 0, 0 );
gl.enableVertexAttribArray( vTexCoord );
gl.uniform1i( gl.getUniformLocation(program3, "texture2"), 0);
gl.clearColor( 1.0, 1.0, 1.0, 1.0 );
gl.viewport(0, 0, 2048, 1024);
render();
}
function render() {
// render to texture
//------------------------------------------------------------------------------------------------------------------------------------------
// SECTION FOR BACKGROUND TEXTURE!
//BACKGROUND TEXTURE: USE PROGRAM 1
gl.useProgram(program1);
gl.clear( gl.COLOR_BUFFER_BIT );
gl.drawArrays( gl.TRIANGLES, 0, 6 );
gl.bindFramebuffer( gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, texture1);
gl.clear( gl.COLOR_BUFFER_BIT );
gl.drawArrays(gl.TRIANGLES, 0, 6);
//------------------------------------------------------------------------------------------------------------------------------------------
//RAIN TEXTURE: USE PROGRAM 2
gl.useProgram(program2);
gl.bindFramebuffer( gl.FRAMEBUFFER, framebuffer);
if(flag) {
gl.bindTexture(gl.TEXTURE_2D, texture2);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture3, 0);
}
else {
gl.bindTexture(gl.TEXTURE_2D, texture3);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture2, 0);
}
//------------------------------------------------------------------------------------------------------------------------------------------
// RAIN TEXTURE: USE PROGRAM 3
gl.useProgram(program3);
gl.drawArrays( gl.TRIANGLES, 0, 6 );
gl.bindFramebuffer( gl.FRAMEBUFFER, null);
if(flag) gl.bindTexture(gl.TEXTURE_2D, texture2);
else gl.bindTexture(gl.TEXTURE_2D, texture2);
gl.clear( gl.COLOR_BUFFER_BIT );
gl.drawArrays(gl.TRIANGLES, 0, 6);
flag = !flag
requestAnimFrame(render);
}
<!DOCTYPE html>
<html>
<script id="vertex-shader1" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 vTexCoord;
varying vec2 fTexCoord;
void main()
{
gl_Position = vPosition;
fTexCoord= vTexCoord;
}
</script>
<script id="vertex-shader2" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 vTexCoord;
varying vec2 fTexCoord;
void main()
{
gl_Position = vPosition;
fTexCoord= vTexCoord;
}
</script>
<script id="vertex-shader3" type="x-shader/x-vertex">
attribute vec4 vPosition;
attribute vec2 vTexCoord;
varying vec2 fTexCoord;
void main()
{
gl_Position = vPosition;
fTexCoord = vTexCoord;
}
</script>
<script id="fragment-shader1" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D texture1;
varying vec2 fTexCoord;
void
main()
{
gl_FragColor= texture2D(texture1, fTexCoord);
}
</script>
<script id="fragment-shader2" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D texture2;
varying vec2 fTexCoord;
void main()
{
gl_FragColor= texture2D(texture2, fTexCoord);
}
</script>
<script id="fragment-shader3" type="x-shader/x-fragment">
precision mediump float;
varying vec2 fTexCoord;
uniform sampler2D texture2;
void main()
{
float y= fTexCoord.y - 0.1;
float x= fTexCoord.x;
if(y < 0.0) {
y = 1.0;
x = fTexCoord.x + 0.1 ;
}
if(x > 1.0){
x = 0.0;
}
gl_FragColor = texture2D( texture2, vec2(x,y));
}
</script>
<script type="text/javascript" src="../Common/webgl-utils.js"></script>
<script type="text/javascript" src="../Common/InitShaders.js"></script>
<script type="text/javascript" src="../Common/MV.js"></script>
<script type="text/javascript" src="testingBG.js"></script>
<img src="Raindrops_scaled.png" id="raindrop" hidden />
<img src="Background.jpg" id="background" hidden />
<body>
<canvas id="gl-canvas" width="2048" height="1024">
Oops ... your browser doesn't support the HTML5 canvas element
</canvas>
</body>
</html>
答案 0 :(得分:1)
我已尝试提供更全面的说明,以解决可能对您有用的解决方案。
由于cors>,这可能无法在Chrome中使用。>
edit:通过手动将图像插入为位图数组来解决问题。 T.T
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
background-color: black;
}
canvas {
display: block;
margin: auto;
border: solid 1px white;
border-radius: 10px;
}
img {
display: none;
}
</style>
</head>
<body>
<canvas id="canvas"></canvas>
<script type="application/javascript">
// I've only placed this here to get around cors < _ >
var images = function() {
"use strict";
function Image(width,height,pixels) {
this.width = width;
this.height = height;
this.pixels = pixels;
}
return {
background: new Image(16,16,new Uint8Array([127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,95,95,95,255,95,95,95,255,127,127,127,255,127,127,127,255,127,127,127,255,95,95,95,255,127,127,127,255,127,127,127,255,95,95,95,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,95,95,95,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,95,95,95,255,127,127,127,255,127,127,127,255,95,95,95,255,127,127,127,255,127,127,127,255,127,127,127,255,127,127,127,255,95,95,95,255,95,95,95,255,95,95,95,255,153,217,234,255,95,95,95,255,127,127,127,255,127,127,127,255,95,95,95,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,95,95,95,255,95,95,95,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,22,116,50,255,22,116,50,255,22,116,50,255,22,116,50,255,22,116,50,255,22,116,50,255,22,116,50,255,22,116,50,255,153,217,234,255,153,217,234,255,153,217,234,255,153,217,234,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255,34,177,76,255])),
raindrop: new Image(8,8,new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0,41,49,151,255,41,49,151,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,41,49,151,255,64,73,202,255,64,73,202,255,41,49,151,255,0,0,0,0,0,0,0,0,0,0,0,0,41,49,151,255,64,73,202,255,64,73,202,255,64,73,202,255,64,73,202,255,41,49,151,255,0,0,0,0,0,0,0,0,41,49,151,255,64,73,202,255,64,73,202,255,64,73,202,255,64,73,202,255,41,49,151,255,0,0,0,0,41,49,151,255,64,73,202,255,64,73,202,255,64,73,202,255,64,73,202,255,64,73,202,255,64,73,202,255,41,49,151,255,41,49,151,255,64,73,202,255,64,73,202,255,64,73,202,255,64,73,202,255,64,73,202,255,64,73,202,255,41,49,151,255,0,0,0,0,41,49,151,255,64,73,202,255,64,73,202,255,64,73,202,255,64,73,202,255,41,49,151,255,0,0,0,0,0,0,0,0,0,0,0,0,41,49,151,255,41,49,151,255,41,49,151,255,41,49,151,255,0,0,0,0,0,0,0,0]))
};
}();
void function() {
"use strict";
// Variables
var canvasWidth = 180;
var canvasHeight = 160;
var canvas = null;
var gl = null;
var raindrops = [];
// WebGL programs (contains code that modifies your vertex data & code that processes each pixel)
var backgroundProgram = null;
var raindropProgram = null;
// WebGL program attributes (indexes into attributes inside the program, webgl uses these to interpret data in the buffers)
var aPositionLocation = 0;
var aUVLocation = 1;
// WebGL program uniforms (It's a variable that's shared between Javascript and the program, you just need to call a function to update it's value)
var uRaindropPosition = null; // vec2
var uRaindropSize = null; // vec2
// WebGL buffers (It's like an array '[]', but it's on the GPU, we'll use it here to specify the properties of triangles we want to draw)
var backgroundBuffer = null;
// WebGL textures
var backgroundTexture = null;
var raindropTexture = null;
// Constructors
// If this function is called with 'new Raindrop(0,0,10);' it'll create a new raindrop object that has these properties
function Raindrop(x,y,size) {
this.x = x;
this.y = y;
this.size = size;
}
// Functions
function createProgram(vertexCode,fragmentCode) {
var program = gl.createProgram();
var vertexShader = gl.createShader(gl.VERTEX_SHADER);
var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
// Upload source code
gl.shaderSource(vertexShader,vertexCode);
gl.shaderSource(fragmentShader,fragmentCode);
// Compile shaders
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
// Check there isn't any problems
try {
if (!gl.getShaderParameter(vertexShader,gl.COMPILE_STATUS)) {
throw "Vertex Shader: " + gl.getShaderInfoLog(vertexShader);
}
if (!gl.getShaderParameter(fragmentShader,gl.COMPILE_STATUS)) {
throw "Fragment Shader: " + gl.getShaderInfoLog(fragmentShader);
}
} catch(log) {
gl.deleteProgram(program);
gl.deleteShader(vertexShader);
gl.deleteShader(vertexShader);
console.error(log);
}
// Attach to the main program
gl.attachShader(program,vertexShader);
gl.attachShader(program,fragmentShader);
gl.linkProgram(program);
gl.deleteShader(vertexShader);
gl.deleteShader(fragmentShader);
return program;
}
function createBuffer(data) {
// Create a GPU buffer, it's a really simple object
// Buffers can be best thought of as a normal array that sits in GPU memory (VRAM)
var buffer = gl.createBuffer();
// attach our new buffer on the gpu to 'ARRAY_BUFFER'
// Think of it as, whatever is attached to 'ARRAY_BUFFER' is the current buffer that get used with WebGl functions
gl.bindBuffer(gl.ARRAY_BUFFER,buffer);
// Upload our array of verticies as 32 bit floats
gl.bufferData(gl.ARRAY_BUFFER,new Float32Array(data),gl.STATIC_DRAW);
// Tell WebGL we're not using the buffer anymore at the moment
gl.bindBuffer(gl.ARRAY_BUFFER,null);
return buffer;
}
function createTexture(image) {
var texture = gl.createTexture();
// attach our new texture on the gpu to 'TEXTURE_2D'
// Think of it as, whatever is attached is our current texture we're using for webgl functions
gl.bindTexture(gl.TEXTURE_2D,texture);
// Set texture parameters, i.e. how are we going to scale it up or down/ what should happen if we
// try to get a pixel from outside the texture
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MAG_FILTER,gl.NEAREST); // Use nearest neighbour when sizing up
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_MIN_FILTER,gl.NEAREST); // Use nearest neighbour when sizing down
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_S,gl.REPEAT); // Apply a modulo to x coordinates if there outside the texture
gl.texParameteri(gl.TEXTURE_2D,gl.TEXTURE_WRAP_T,gl.REPEAT); // Apply a modulo to y coordinates if there outside the texture
// Upload our image to our gpu texture
gl.texImage2D(
gl.TEXTURE_2D, // I want it to get uploaded to what's attached to TEXTURE_2D
0, // Mipmap level, it can stay at 0
gl.RGBA, // Data format the gpu texture will use, in this case 8 bit RGBA for each pixel
image.width,
image.height,
0, // Border, always should be zero
gl.RGBA, // The data format of the image we're uploading to the gpu
gl.UNSIGNED_BYTE, // Datatype of each actual colour channel, here it's going to be an unsigned 8 bit int that will go from 0-255
image.pixels // The image we finally want to upload!, any HTML image element will work just fine
);
// Now we'll unbind the texture since we are done with it now
gl.bindTexture(gl.TEXTURE_2D,null); // Passing null tells WebGL that we're not currently 'using' any texture
return texture;
}
// Convert from Javascript canvas coordinate system to WebGL's normalized device coordinates
/*
The position 0,0 moves to the center of the screen.
The top left corner becomes -1,1
The bottom right corner becomes 1,-1
Coordinates need to be converted before they can be passed to WebGL
*/
function convertXCoordinate(x) {
return (x / canvasWidth) * 2.0 - 1.0;
}
function convertYCoordinate(y) {
return ((canvasHeight - y) / canvasHeight) * 2.0 - 1.0;
}
function loop() {
// Update
// Update raindrop positions
// Just to simulate rain 'falling'
for (var i = 0; i < raindrops.length; ++i) {
var raindrop = raindrops[i];
raindrop.y += 2.5;
if (raindrop.y > canvasHeight) {
raindrop.x = Math.random() * canvasWidth;
raindrop.y = 0;
}
}
// Render
// Clear the canvas
gl.clear(gl.COLOR_BUFFER_BIT);
// Draw the background
gl.bindBuffer(gl.ARRAY_BUFFER,backgroundBuffer);
// We use this function to tell WebGL how to interpret the data in the current buffer
gl.vertexAttribPointer(
aPositionLocation, // attribute index, usually just the order they were declared in the vertex shader
2, // Number of values in attribute, vec2 has two floats
gl.FLOAT, // The kind of data to be expected in the buffer
false, // Telling WebGL we don't want the data to get normalized to 0.0 -> 1.0
Float32Array.BYTES_PER_ELEMENT * 4, // This is the size of a complete vertex in bytes (2 vec2's so four floats)
Float32Array.BYTES_PER_ELEMENT * 0 // This is the 'offset', or the number of floats that come before this attribute in each vertex. aPosition comes first so it's zero
);
gl.vertexAttribPointer(
aUVLocation,
2,
gl.FLOAT,
false,
Float32Array.BYTES_PER_ELEMENT * 4,
Float32Array.BYTES_PER_ELEMENT * 2 // aUV comes second, so it's offset is two floats
);
// Tell WebGL we want to turn these attributes on for rendering
gl.enableVertexAttribArray(aPositionLocation);
gl.enableVertexAttribArray(aUVLocation);
// Attach our background texture so the sampler2D can use it
gl.bindTexture(gl.TEXTURE_2D,backgroundTexture);
// Attach our program we want to render with
gl.useProgram(backgroundProgram);
// Tell WebGL to start rendering!
gl.drawArrays(
gl.TRIANGLES, // Takes groups of 3 verticies to draw one complete triangle
0, // Start at index 0 in our buffer
6 // Use six complete verticies (whole sets of attributes) to draw 2 triangles
);
// We no longer need the background buffer, program or texture
gl.bindBuffer(gl.ARRAY_BUFFER,null);
gl.disableVertexAttribArray(aPositionLocation);
gl.disableVertexAttribArray(aUVLocation);
gl.bindTexture(gl.TEXTURE_2D,null);
gl.useProgram(null);
// Draw raindrops
// Since we're going to pass coordinates through uniforms, this can be done without a buffer
// Bind texture for sampler2D
gl.bindTexture(gl.TEXTURE_2D,raindropTexture);
// After attaching a program, we can upload values to uniforms using the values we got before from getUniformLocation()
gl.useProgram(raindropProgram);
for (var i = 0; i < raindrops.length; ++i) {
var raindrop = raindrops[i];
// Upload position
gl.uniform2f(
uRaindropPosition,
convertXCoordinate(raindrop.x),
convertYCoordinate(raindrop.y)
);
// Upload size
gl.uniform1f(
uRaindropSize,
raindrop.size
);
gl.drawArrays(gl.POINTS,0,1);
}
//
requestAnimationFrame(loop);
}
// This gets called when the page finishes loading
onload = function() {
// Get the canvas
canvas = document.getElementById("canvas");
canvas.width = canvasWidth;
canvas.height = canvasHeight;
// Don't forget the webgl context!
gl = canvas.getContext("webgl");
// Create our program, it's made up of two smaller programs.
// One processes verticies (one whole set of attributes) & one that decides the colour of each pixel
// These smaller programs need to be created, compiled then attached to the main program
// modern Javascript has a nice feature called string templates, if we use `` instead of "" we can
// write strings that can have multiple lines!
backgroundProgram = createProgram(
` // Vertex Shader
precision lowp float;
// These values are filled from our buffer
attribute vec2 aPosition;
attribute vec2 aUV;
// Anything passed to this variable gets interpolated across any
// pixels inside our triangle
varying vec2 vUV;
void main() {
vUV = aUV;
gl_Position = vec4(aPosition,0.0,1.0);
}
`,` // Fragment shader
precision lowp float;
// Freshly interpolated value from our vertex shader
varying vec2 vUV;
// A uniform is a variable shared between webgl & javascript
// A sampler2D is a special datatype we use with texture2D
// to get a single pixel from the texture that's currently
// attached to TEXTURE_2D
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture,vUV);
}
`
);
raindropProgram = createProgram(
` // Vertex shader
precision lowp float;
attribute vec2 aPosition;
uniform vec2 uRaindropPosition;
uniform float uRaindropSize;
void main() {
gl_PointSize = uRaindropSize;
gl_Position = vec4(uRaindropPosition + aPosition,0.0,1.0);
}
`,` // Fragment shader
precision lowp float;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D(uTexture,gl_PointCoord);
// Discard the pixel if it has no alpha
if (gl_FragColor.a == 0.0) {
discard;
}
}
`
);
// Get the 'location' of uniform variables in our raindrop program
// These will be used later to upload new uniform values to the gpu
uRaindropPosition = gl.getUniformLocation(raindropProgram,"uRaindropPosition");
uRaindropSize = gl.getUniformLocation(raindropProgram,"uRaindropSize");
/*
Create and upload an array of data to the GPU,
each line contains all the attribute that make up one
vertex, here it's an x y coorinate for the position on the screen
followed by another x y coordinate for the position of the texture that
should be displayed at that corner (coordinates for textures are normally called UVs U = x, V = y)
Below is a list of verticies that make up two triangles that forms a rectangle that covers the whole screen,
triangles in WebGL should go in counter clockwise order
*/
backgroundBuffer = createBuffer([
// X Y U V
-1.0, 1.0, 0.0,0.0, // Top left
-1.0,-1.0, 0.0,1.0, // Bottom left
1.0, 1.0, 1.0,0.0, // Top right
1.0, 1.0, 1.0,0.0, // Top right
-1.0,-1.0, 0.0,1.0, // Top left
1.0,-1.0, 1.0,1.0 // Bottom right
]);
// Create & upload textures
backgroundTexture = createTexture(images.background);
raindropTexture = createTexture(images.raindrop);
// Set clearcolour, when we want an empty canvas
// R G B A
gl.clearColor(0.5,0.5,0.5,1.0);
// Create some randomly placed raindrops to simulate
for (var i = 0; i < 10; ++i) {
raindrops.push(
new Raindrop(
Math.random() * canvasWidth,
Math.random() * canvasHeight,
25.0
)
);
}
loop();
}
// Called when the page is reloading or getting closed
onunload = function() {
gl.deleteProgram(backgroundProgram);
gl.deleteProgram(raindropProgram);
gl.deleteBuffer(backgroundBuffer);
gl.deleteTexture(backgroundTexture);
gl.deleteTexture(raindropTexture);
}
}();
</script>
</body>
</html>