WebGL学习【焕新计划】

2024-06-16 16:52
文章标签 学习 计划 webgl 焕新

本文主要是介绍WebGL学习【焕新计划】,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

WebGL基础

在正式进入webgl之前,我想有必要简单了解一下渲染管线,毕竟它贯穿webgl学习的整个过程。

渲染管线流程图:

webgl着色器简单语法:

在GLSL(OpenGL Shading Language)中,常见的变量类型可以归纳为以下几种,并根据提供的参考文章进行详细的解释:

  1. 基本数据类型
    • 整型(int):用于存放自然数的变量。
    • 浮点型(float):用于存放小数点数值的变量。
    • 布尔型(bool):用于存放0值或非0值,0代表false,1代表true。
  2. 向量(vec)
    • GLSL中的向量类型可以是2维(vec2)、3维(vec3)或4维(vec4)。
    • 向量中的数据类型可以是整数、浮点数或布尔值。
    • 向量在GLSL中常用于描述颜色、坐标和纹理等。
  3. GLSL内置变量
    • 顶点属性(attribute):指顶点的信息,如顶点坐标、纹理坐标、颜色等。GLSL会为这些传统的顶点属性设置内置变量与之对应,以便在需要时可以在顶点或片元着色器中直接引用。
    • 一致变量(uniform):是外部应用程序传递给(顶点和片元)着色器的变量。它在着色器程序内部是不可修改的,通常用于表示变换矩阵、材质、光照参数和颜色等信息。
    • 易变变量(varying):由顶点程序向片元程序插值传递的变量。这些变量在顶点着色器中定义,并在片元着色器中使用,用于传递从顶点计算得到的插值数据。
    • 其他内置变量:如gl_ModelViewMatrix(模型视图变换矩阵)、gl_ProjectMatrix(投影矩阵)等,这些变量描述了OpenGL的状态,并在着色器之间共享。
  4. 特殊变量
    • gl_Position:在顶点着色器中用于写顶点位置,其类型是highp vec4
    • gl_FragColor:在片元着色器中用于写片元颜色,其类型是mediump vec4
    • gl_PointSize:在顶点着色器中用于写光栅化后的点大小,其类型是mediump float

向量与矩阵

常见内置变量

常见数据类型

常见修饰符

vec3

gl_PointSize

float

attribute

vec4

gl_Position

int

uniform

mat3

gl_FragColor

bool

varying

mat4

gl_FragCoord

sampler2D

gl_ModelViewMatrix
gl_ProjectMatrix

说明:

  • in和out

   glsl版本不同,attribute和uniform在glsl 新本本对应着inout是两种特殊的变量限定符,它们用于在着色器阶段之间传递数据。

  • in限定符
  • 用途in用于在着色器阶段之间输入数据。在顶点着色器中,in变量通常表示从CPU传入的顶点数据,如顶点位置、纹理坐标等。在片元着色器中,in变量表示从顶点着色器插值传递过来的数据。

  • 示例

// 顶点着色器示例  
#version 330 core  
layout(location = 0) in vec3 aPos;    // aPos是一个in变量,表示顶点位置  
out vec4 vertexColor;                 // vertexColor是一个out变量,用于将颜色数据传递给片元着色器  void main() {  gl_Position = vec4(aPos, 1.0);    // 设置顶点位置  vertexColor = vec4(1.0, 0.0, 0.0, 1.0); // 设置顶点颜色为红色  
}
  •  out限定符
  • 用途out用于在着色器阶段之间输出数据。在顶点着色器中,out变量用于将计算得到的数据传递给下一个着色器阶段(通常是片元着色器)。在片元着色器中,out变量表示最终的像素颜色或其他输出数据。

  • 示例

// 片元着色器示例  
#version 330 core  
in vec4 vertexColor;    // vertexColor是一个in变量,从顶点着色器接收颜色数据  
out vec4 FragColor;     // FragColor是一个out变量,表示最终的像素颜色  void main() {  FragColor = vertexColor; // 直接将顶点着色器传递过来的颜色作为最终像素颜色  
}
  • 坐标变换:【3D基础】坐标转换——地理坐标投影到平面_将世界地理坐标投影到平面的方法-CSDN博客
  • Three.js和Cesium.js中坐标_threejs 物体缩放后需要更新世界坐标吗-CSDN博客
  • 最后在正式开始前,先大致了解整个流程:
  • 整个流程:

 

 

 (注:上述内容为搭建一个框架,此外课程学习前可能需一些计算机图形学和线性代数的基本知识,若还未学习相关内容的小伙伴可以去B站学习相关内容,推荐《计算机图形学入门》和《线性代数的本质》两门课程,当然学习webgl还是很推荐郭老师的课程哈!课程+博客!)

了解大致流程后,开始正式webgl入门课程的学习:

1.绘制一个点

1.1实现效果:

1.2实现代码:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>使用WebGL绘制一个点</title></head><body><!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布--><canvas id="webgl" width="500" height="500" style="background-color: greenyellow"></canvas>
<!-- 写法一(推荐) -->
<!-- 顶点着色器源码 -->
<script id="vertexShader" type="x-shader/x-vertex">void main() {//给内置变量gl_PointSize赋值像素大小gl_PointSize=20.0;//顶点位置,位于坐标原点gl_Position =vec4(0.0,0.0,0.0,1.0);}</script><!-- 片元着色器源码 --><script id="fragmentShader" type="x-shader/x-fragment">void main() {gl_FragColor = vec4(1.0,0.0,0.0,1.0);}</script><!-- 写法二(仅大致了解) --><!-- //顶点着色器源码var vertexShaderSource = '' +'void main(){' +//给内置变量gl_PointSize赋值像素大小'   gl_PointSize=10.0;' +//顶点位置,位于坐标原点'   gl_Position =vec4(0.0,0.0,0.0,1.0);' +'}';//片元着色器源码var fragShaderSource = '' +'void main(){' +//定义片元颜色'   gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +'}'; --><script>//通过getElementById()方法获取canvas画布var canvas=document.getElementById('webgl');//通过方法getContext()获取WebGL上下文var gl=canvas.getContext('webgl');//顶点着色器源码var vertexShaderSource = document.getElementById( 'vertexShader' ).innerText;//片元着色器源码var fragShaderSource = document.getElementById( 'fragmentShader' ).innerText;//初始化着色器var program = initShader(gl,vertexShaderSource,fragShaderSource);//开始绘制,显示器显示结果gl.drawArrays(gl.POINTS,0,1);//声明初始化着色器函数function initShader(gl,vertexShaderSource,fragmentShaderSource){//创建顶点着色器对象var vertexShader = gl.createShader(gl.VERTEX_SHADER);//创建片元着色器对象var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);//引入顶点、片元着色器源代码gl.shaderSource(vertexShader,vertexShaderSource);gl.shaderSource(fragmentShader,fragmentShaderSource);//编译顶点、片元着色器gl.compileShader(vertexShader);gl.compileShader(fragmentShader);//创建程序对象programvar program = gl.createProgram();//附着顶点着色器和片元着色器到programgl.attachShader(program,vertexShader);gl.attachShader(program,fragmentShader);//链接programgl.linkProgram(program);//使用programgl.useProgram(program);//返回程序program对象return program;}</script></body></html>

1.3解析:刚入门第一个案例较为简单,通过本案例了解代码编写整个流程即可

2. 绘制一个矩形

2.1实现效果:

2.2实现代码:
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><title>使用WebGL绘制一个矩形</title></head><body><!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布--><canvas id="webgl" width="500" height="500" style="background-color: greenyellow"></canvas><!-- 顶点着色器源码 -->
<script id="vertexShader" type="x-shader/x-vertex">//非单点,用类型化数组  //attribute声明vec4类型变量apos attribute vec4 apos;void main() {//顶点坐标apos赋值给内置变量gl_Position//逐顶点处理数据gl_Position = apos;}</script><!-- 片元着色器源码 --><script id="fragmentShader" type="x-shader/x-fragment">void main() {gl_FragColor = vec4(1.0,0.0,0.0,1.0);}</script><script>//通过getElementById()方法获取canvas画布var canvas=document.getElementById('webgl');//通过方法getContext()获取WebGL上下文var gl=canvas.getContext('webgl');//顶点着色器源码var vertexShaderSource = document.getElementById( 'vertexShader' ).innerText;//片元着色器源码var fragShaderSource = document.getElementById( 'fragmentShader' ).innerText;//初始化着色器var program = initShader(gl,vertexShaderSource,fragShaderSource);//获取顶点着色器的位置变量ap os,即aposLocation指向apos变量。var aposLocation = gl.getAttribLocation(program,'apos');//类型数组构造函数Float32Array创建顶点数组var data=new Float32Array([0.5,0.5,-0.5,0.5,-0.5,-0.5,0.5,-0.5]);//创建缓冲区对象var buffer=gl.createBuffer();//绑定缓冲区对象,激活buffer// gl.bindBuffer(gl.ARRAY_BUFFER,buffer);gl.bindBuffer(gl.ARRAY_BUFFER,buffer);//顶点数组data数据传入缓冲区gl.bufferData(gl.ARRAY_BUFFER,data,gl.STATIC_DRAW);//缓冲区中的数据按照一定的规律传递给位置变量aposgl.vertexAttribPointer(aposLocation,2,gl.FLOAT,false,0,0);//允许数据传递gl.enableVertexAttribArray(aposLocation);//开始绘制,显示器显示结果gl.drawArrays(gl.LINE_LOOP,0,4);//声明初始化着色器函数function initShader(gl,vertexShaderSource,fragmentShaderSource){//创建着色器对象var vertexShader = gl.createShader(gl.VERTEX_SHADER);var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);//引入着色器源代码gl.shaderSource(vertexShader,vertexShaderSource);gl.shaderSource(fragmentShader,fragmentShaderSource);//编译着色器gl.compileShader(vertexShader);gl.compileShader(fragmentShader);//创建程序对象programvar program = gl.createProgram();//附着着色器到programgl.attachShader(program,vertexShader);gl.attachShader(program,fragmentShader);//链接programgl.linkProgram(program);//使用programgl.useProgram(program);//返回程序program对象return program;}</script></body></html>

2.3解析:本案例,在之前案例的基础上,增加了声明变量和为变量传递数据的过程。在webgl中先需创建一个类型化数组存储“顶点”信息,然后在绘制的过程,指定绘制的类型,这时进行图元装配时就会将点按指定要求绘制成几何体。如本案例就是绘制类型gl.LINE_LOOP(闭合线)就会将点连城闭合线(矩形)

3.绘制三角形|投影(UV坐标)

3.1实现效果

[-1.0-1.0]

3.2实现代码
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>绘制三角形|投影</title>
</head><body><!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布--><canvas id="webgl" width="500" height="500" style="background-color: greenyellow"></canvas><!-- 顶点着色器源码 --><script id="vertexShader" type="x-shader/x-vertex">//非单点,用类型化数组  //attribute声明vec4类型变量apos attribute vec4 apos;void main() {//顶点坐标apos赋值给内置变量gl_Position//逐顶点处理数据gl_Position=apos; }</script><!-- 片元着色器源码 --><script id="fragmentShader" type="x-shader/x-fragment">void main() {gl_FragColor = vec4(1.0,0.0,0.0,1.0);}</script><script>//通过getElementById()方法获取canvas画布var canvas = document.getElementById('webgl');//通过方法getContext()获取WebGL上下文var gl = canvas.getContext('webgl');//顶点着色器源码var vertexShaderSource = document.getElementById('vertexShader').innerText;//片元着色器源码var fragShaderSource = document.getElementById('fragmentShader').innerText;//初始化着色器var program = initShader(gl, vertexShaderSource, fragShaderSource);//获取顶点着色器的位置变量apos,即aposLocation指向apos变量。var aposLocation = gl.getAttribLocation(program, 'apos');//类型数组构造函数Float32Array创建顶点数组var data = new Float32Array([// 将对z轴进行投影,投影为二维平面,所以第一个顶点的1.0,可以取值[-1.0,1.0],效果都一样,若超过1.0或少于-1.0,测试不难发现会有部分被裁掉,因为在webgl中坐标为[-1.0,1.0]0.0, 0.0, 2.0, //三角形顶点1坐标0.0, 1.0, 0.0, //三角形顶点2坐标1.0, 0.0, 0.0 //三角形顶点3坐标]);//创建缓冲区对象var buffer = gl.createBuffer();//绑定缓冲区对象,激活buffer// gl.bindBuffer(gl.ARRAY_BUFFER,buffer);gl.bindBuffer(gl.ARRAY_BUFFER, buffer);//顶点数组data数据传入缓冲区gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);//缓冲区中的数据按照一定的规律传递给位置变量aposgl.vertexAttribPointer(aposLocation, 3, gl.FLOAT, false, 0, 0);//允许数据传递gl.enableVertexAttribArray(aposLocation);//开始绘制,显示器显示结果gl.drawArrays(gl.TRIANGLES, 0, 3);//声明初始化着色器函数function initShader(gl, vertexShaderSource, fragmentShaderSource) {//创建着色器对象var vertexShader = gl.createShader(gl.VERTEX_SHADER);var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);//引入着色器源代码gl.shaderSource(vertexShader, vertexShaderSource);gl.shaderSource(fragmentShader, fragmentShaderSource);//编译着色器gl.compileShader(vertexShader);gl.compileShader(fragmentShader);//创建程序对象programvar program = gl.createProgram();//附着着色器到programgl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);//链接programgl.linkProgram(program);//使用programgl.useProgram(program);//返回程序program对象return program;}</script>
</body></html>

3.3解析:本案例将对z轴进行投影,投影为二维平面,所以第一个顶点的1.0,可以取值[-1.0,1.0],效果都一样,若超过1.0或少于-1.0,测试不难发现会有部分被裁掉,因为在webgl中坐标为[-1.0 ,1.0],所以效果左边的图设置三角形顶点1的z值[-1.0,1.0]皆是那个效果,右图为z值为2.0的效果

中级案例:

绘制一个时钟

 1.实现效果:

2.实现代码:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta http-equiv="X-UA-Compatible" content="IE=edge"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>时针</title><style>#myCanvas{background-color: rgb(246, 246, 249);}</style><script src="glMatrix-0.9.6.min.js"></script><script>let vertexstring = `attribute vec4 a_position;uniform mat4 u_formMatrix;void main(void){gl_Position =u_formMatrix * a_position;}`;let fragmentstring = `precision mediump float;void main(void){gl_FragColor =vec4(0.0,0.0,0.0,1.0);}`;var projMat4 = mat4.create();var webgl;var uniformTexture = 0;var uniformTexture1 = 0;var uniformAnim = 0;var count = 0;function webglStart() {init();tick();}function tick() {window.setTimeout(tick, 1000);initbuffer(0.8);initbuffer(0.5);initbuffer(0.4);count = count +1;};function init() {initWebgl();initShader();}function initWebgl() {let webglDiv = document.getElementById('myCanvas');webgl = webglDiv.getContext("webgl");webgl.viewport(0, 0, webglDiv.clientWidth, webglDiv.clientHeight);//mat4.ortho(0, webglDiv.clientWidth, webglDiv.clientHeight, 0, -1.0, 1.0, projMat4)}function initShader() {let vsshader = webgl.createShader(webgl.VERTEX_SHADER);let fsshader = webgl.createShader(webgl.FRAGMENT_SHADER);webgl.shaderSource(vsshader, vertexstring);webgl.shaderSource(fsshader, fragmentstring);webgl.compileShader(vsshader);webgl.compileShader(fsshader);if (!webgl.getShaderParameter(vsshader, webgl.COMPILE_STATUS)) {var err = webgl.getShaderInfoLog(vsshader);alert(err);return;}if (!webgl.getShaderParameter(fsshader, webgl.COMPILE_STATUS)) {var err = webgl.getShaderInfoLog(fsshader);alert(err);return;}let program = webgl.createProgram();webgl.attachShader(program, vsshader);webgl.attachShader(program, fsshader)webgl.linkProgram(program);webgl.useProgram(program);webgl.program = program}function initbuffer(radius){//秒分时针,根据长度分且角度需合理设置let angle;if(radius>0.6){//角度为负值angle = -Math.PI/30 *count;}else if(radius>0.4){angle = -Math.PI/1800 *count;}else{angle = -Math.PI/1800 *count *5/60;// angle = -Math.PI/30 *count;}const cos = Math.cos(angle)*radius;const sin = Math.sin(angle)*radius;var matArr= new Float32Array([cos,sin,0,0,-sin,cos,0,0,0,0,1,0,0,0,0,1]);let arr = [0, 0, 0, 1,0, 0.05, 0, 1,radius, 0, 0, 1,radius, 0, 0, 1,0, 0.05, 0, 1,radius, 0.05, 0, 1]let pointPosition = new Float32Array(arr);let aPsotion = webgl.getAttribLocation(webgl.program, "a_position");let triangleBuffer = webgl.createBuffer();webgl.bindBuffer(webgl.ARRAY_BUFFER, triangleBuffer);webgl.bufferData(webgl.ARRAY_BUFFER, pointPosition, webgl.STATIC_DRAW);webgl.enableVertexAttribArray(aPsotion);webgl.vertexAttribPointer(aPsotion, 4, webgl.FLOAT, false, 0, 0);let uniformMatrix = webgl.getUniformLocation(webgl.program, "u_formMatrix");webgl.uniformMatrix4fv(uniformMatrix, false, matArr)webgl.drawArrays(webgl.TRIANGLES, 0, 6);}
3.核心讲解:

3.1动画设置:每隔1秒钟绘制新的图形(图形不断进行旋转)

 function tick() {window.setTimeout(tick, 1000);initbuffer(0.8);initbuffer(0.5);initbuffer(0.4);count = count +1;};

3.2时针旋转角度计算、旋转矩阵构建:根据常识计算旋转角度,需要注意的是这角度为负值,旋转方向才为顺时针,旋转矩阵可看作绕Z轴旋转所得的矩阵

           //秒分时针,根据长度分且角度需合理设置let angle;if(radius>0.6){//角度为负值angle = -Math.PI/30 *count;}else if(radius>0.4){angle = -Math.PI/1800 *count;}else{angle = -Math.PI/1800 *count *5/60;// angle = -Math.PI/30 *count;}const cos = Math.cos(angle)*radius;const sin = Math.sin(angle)*radius;var matArr= new Float32Array([cos,sin,0,0,-sin,cos,0,0,0,0,1,0,0,0,0,1]);

多层纹理实现雾效果

 1.实现效果:

2.实现代码:
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>多重纹理</title>
</head><body><!--canvas标签创建一个宽高均为500像素,背景为蓝色的矩形画布--><canvas id="webgl" width="500" height="500" style="background-color: greenyellow"></canvas><!-- 顶点着色器源码 --><script id="vertexShader" type="x-shader/x-vertex">attribute vec4 a_Position;//顶点位置坐标attribute vec2 a_TexCoord;//纹理坐标varying vec2 v_TexCoord;//插值后纹理坐标void main() {//顶点坐标apos赋值给内置变量gl_Positiongl_Position = a_Position;//纹理坐标插值计算v_TexCoord = a_TexCoord;}</script><!-- 片元着色器源码 --><script id="fragmentShader" type="x-shader/x-fragment">//所有float类型数据的精度是highpprecision highp float;// 接收插值后的纹理坐标varying vec2 v_TexCoord;// 纹理图片像素数据uniform sampler2D u_Sampler;uniform sampler2D u_Sampler1;uniform float u_Ani;void main() {// 采集纹素,逐片元赋值像素值vec4 color1 = texture2D(u_Sampler,v_TexCoord);vec4 color2 = texture2D(u_Sampler1,vec2(v_TexCoord.x + u_Ani , v_TexCoord.y));gl_FragColor = color1 + color2;}</script><script>//通过getElementById()方法获取canvas画布var canvas = document.getElementById('webgl');//通过方法getContext()获取Gl上下文var gl = canvas.getContext('webgl');//顶点着色器源码var vertexShaderSource = document.getElementById('vertexShader').innerText;//片元着色器源码var fragShaderSource = document.getElementById('fragmentShader').innerText;var count = 0;//初始化着色器var program = initShader(gl, vertexShaderSource, fragShaderSource);var a_Position = gl.getAttribLocation(program, 'a_Position');var a_TexCoord = gl.getAttribLocation(program, 'a_TexCoord');var u_Sampler = gl.getUniformLocation(program, 'u_Sampler');var u_Sampler1 = gl.getUniformLocation(program, 'u_Sampler1');var u_Ani = gl.getUniformLocation(program, 'u_Ani');//拉伸改变其坐标值var data = new Float32Array([-0.5, 0.5, //左上角——v0-0.5, -0.5, //左下角——v10.5, 0.5, //右上角——v20.5, -0.5 //右下角——v3]);/*** 创建UV纹理坐标数据textureData**/var textureData = new Float32Array([0, 1, //左上角——uv00, 0, //左下角——uv11, 1, //右上角——uv21, 0 //右下角——uv3]);//创建缓冲区对象var buffer = gl.createBuffer();//绑定缓冲区对象,激活buffergl.bindBuffer(gl.ARRAY_BUFFER, buffer);//顶点数组data数据传入缓冲区gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);//缓冲区中的数据按照一定的规律传递给位置变量aposgl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);//允许数据传递gl.enableVertexAttribArray(a_Position);//创建缓冲区对象var textureBuffer = gl.createBuffer();//绑定缓冲区对象,激活buffergl.bindBuffer(gl.ARRAY_BUFFER, textureBuffer);//顶点数组data数据传入缓冲区gl.bufferData(gl.ARRAY_BUFFER, textureData, gl.STATIC_DRAW);//缓冲区中的数据按照一定的规律传递给位置变量aposgl.vertexAttribPointer(a_TexCoord, 2, gl.FLOAT, false, 0, 0);//允许数据传递gl.enableVertexAttribArray(a_TexCoord);//要调用呀!写了函数不用是什么鬼loop();/**创建缓冲区textureBuffer,传入图片纹理数据,然后执行绘制方法drawArrays()**/texture1 = initTexture("fog.png");texture0 = initTexture("山水.png");function handleLoadedTexture(texture) {gl.bindTexture(gl.TEXTURE_2D, texture);gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);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);// 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);}function initTexture(imageFile) {let textureHandle = gl.createTexture();textureHandle.image = new Image();textureHandle.image.src = imageFile;textureHandle.image.onload = function () {handleLoadedTexture(textureHandle)}return textureHandle;}function loop() {requestAnimationFrame(loop);draw();}function draw() {gl.clearColor(0.0, 1.0, 0.0, 1.0);gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);gl.enable(gl.DEPTH_TEST);count = count + 0.01;// console.log(count);gl.uniform1f(u_Ani, count);gl.activeTexture(gl.TEXTURE0);gl.bindTexture(gl.TEXTURE_2D, texture0);gl.uniform1i(u_Sampler, 0);gl.activeTexture(gl.TEXTURE1);gl.bindTexture(gl.TEXTURE_2D, texture1);gl.uniform1i(u_Sampler1, 1);gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);}//声明初始化着色器函数function initShader(gl, vertexShaderSource, fragmentShaderSource) {//创建着色器对象var vertexShader = gl.createShader(gl.VERTEX_SHADER);var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);//引入着色器源代码gl.shaderSource(vertexShader, vertexShaderSource);gl.shaderSource(fragmentShader, fragmentShaderSource);//编译着色器gl.compileShader(vertexShader);gl.compileShader(fragmentShader);//创建程序对象programvar program = gl.createProgram();//附着着色器到programgl.attachShader(program, vertexShader);gl.attachShader(program, fragmentShader);//链接programgl.linkProgram(program);//使用programgl.useProgram(program);//返回程序program对象return program;};</script>
</body></html>
3.核心拆解:

3.1 片元着色器中调用 texture2D(texture,texcoord),方法有两个参数,一个为所需要的纹理,另一部分为纹理坐标。纹理坐标好办,创建类型化数组传递即可。本案例涉及多重纹理叠加,所以最后片源着色器的颜色为两种颜色的和。

       vec4 color1 = texture2D(u_Sampler,v_TexCoord);vec4 color2 = texture2D(u_Sampler1,vec2(v_TexCoord.x + u_Ani , v_TexCoord.y));gl_FragColor = color1 + color2;

本案例还涉及动态雾,所以需要利用requestAnimationFrame()方法,进行不断绘制。

function tick() {requestAnimFrame(tick)draw();};

下面重点讲述纹理数据的创建与传递,纹理的创建可参考缓冲区的创建:

1.webgl.createTexture创建纹理对象

2.配置纹理对象的属性 .image   .image.src   .image.onload

3.绑定纹理、设置二维纹理、设置纹理属性,将其封装归类到handleLoaderTexture()方法中

4.最后在绘制函数draw()中激活、绑定、传递纹理数据

        function handleLoadedTexture(texture) {webgl.bindTexture(webgl.TEXTURE_2D, texture);webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGBA, webgl.RGBA, webgl.UNSIGNED_BYTE, texture.image);webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.LINEAR);webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.LINEAR);webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.REPEAT);webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.REPEAT);// webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE);// webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.CLAMP_TO_EDGE);}function initTexture(imageFile, num) {let textureHandle = webgl.createTexture();textureHandle.image = new Image();textureHandle.image.src = imageFile;textureHandle.image.onload = function () {handleLoadedTexture(textureHandle, num)}return textureHandle;}function draw() {webgl.clearColor(0.0, 1.0, 0.0, 1.0);webgl.clear(webgl.COLOR_BUFFER_BIT | webgl.DEPTH_BUFFER_BIT);webgl.enable(webgl.DEPTH_TEST);//纹理变动uniformAnim = webgl.getUniformLocation(webgl.program, "anim");count = count + 0.01;webgl.uniform1f(uniformAnim, count);webgl.activeTexture(webgl.TEXTURE0);webgl.bindTexture(webgl.TEXTURE_2D, texture0);webgl.uniform1i(uniformTexture, 0);webgl.activeTexture(webgl.TEXTURE1);webgl.bindTexture(webgl.TEXTURE_2D, texture1);webgl.uniform1i(uniformTexture1, 1);webgl.drawArrays(webgl.TRIANGLES, 0, 6);}

(注:在WebGL渲染纹理时,纹理的像素也有要求的,需要为2的幂次方,所以有时用自己的纹理图片去贴图时,明明代码都一样却总出来一个黑色照片,不是代码的问题,是原始数据的问题)

这篇关于WebGL学习【焕新计划】的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1067005

相关文章

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

Java深度学习库DJL实现Python的NumPy方式

《Java深度学习库DJL实现Python的NumPy方式》本文介绍了DJL库的背景和基本功能,包括NDArray的创建、数学运算、数据获取和设置等,同时,还展示了如何使用NDArray进行数据预处理... 目录1 NDArray 的背景介绍1.1 架构2 JavaDJL使用2.1 安装DJL2.2 基本操

Oracle数据库执行计划的查看与分析技巧

《Oracle数据库执行计划的查看与分析技巧》在Oracle数据库中,执行计划能够帮助我们深入了解SQL语句在数据库内部的执行细节,进而优化查询性能、提升系统效率,执行计划是Oracle数据库优化器为... 目录一、什么是执行计划二、查看执行计划的方法(一)使用 EXPLAIN PLAN 命令(二)通过 S

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss