WebGL+Three.js—第一章 WebGL简单应用

1.1 canvas和webgl的区别

1.1.1 <canvas>画布

    <canvas>是HTML5新增的一个DOM元素

    用途:显示二维三维的图像。

    绘制:二维图形可以使用(Canvas API 或 WebGL API)

               三维图形使用WebGL API

1.1.2 Canvas API

    1、Canvas API提供二维绘图的方式

    2、图形的绘制主要通过CanvasRenderingContext2D接口完成

    3、canvas.getContext('2d')

1.1.3 WebGL API

    1、Canvas API提供二维绘图的方式

    2、图形的绘制主要通过WebGlRenderingContext接口完成

    3、canvas.getContext('webgl')

1.1.4 WebGL2RenderingContext

    WebGL2RenderingContext是用来使用WebGL3.0的,它其实是在WebGlRenderingContext的接口之上做一些拓展。

    我们可以在getContext通过webgl2作为参数来获取它的实例:canvas.getContext('webgl2')

1.2 认识webgl,了解webgl

1.2.1 什么是webgl

    webgl是一种3D绘图协议,衍生于OpenGL ES2.0,可以结合HTML5和JavaScript在网页上绘制和渲染二/三维图形。

1.2.2 为什么学webgl

    1、数据可视化(echarts)

    2、图形/游戏引擎

    3、交互演示、图形渲染

    4、地图

    5、VR

    6、物品展示

    7、室内设计

    8、城市规划

1.2.3 webgl的优势

    1、内嵌在浏览器中,不需要安装任何插件即可运行

    2、只需要一个文本编辑器和浏览器,就可以编写三维图形程序

    3、学习和使用比较简单

1.2.4 webgl开源框架

    1、Three.js:JavaScript 3D WebGL库

    2、Babylon.js:Web3D图形引擎

    3、KickJS:Web的开源图形和游戏引擎

    4、ClayGL:构建可扩展的Web3D应用程序

    5、PlayCanvas:网络游戏和3D图形引擎

    6、WebGLStudio.js和Litescene.js:开源Web 3D图形编辑器和创建器

    7、Luma:Uber的3D WebGL可视化库

    8、A-Frame:用于创建VR(虚拟现实)体验的Web框架

1.3 webgl入门—给画布换个颜色

1.3.1 回顾<canvas>

    1、创建<canvas>标签

<canvas id="canvas" width="400" height="400">
  此浏览器不支持canvas
</canvas>

    2、获取canvas上下文

<script>
  const ctx = document.getElementById('canvas');
  const c = ctx.getContext('2d');
</script>

    3、绘制图形

<script>
  const ctx = document.getElementById('canvas');
  const c = ctx.getContext('2d');
  c.fillStyle = 'red'
  c.fillRect(10, 10, 100, 100);
</script>

WebGL+Three.js—第一章 WebGL简单应用

1.3.2 创建webgl

    1、创建<canvas>标签

<canvas id="webgl" width="400" height="400">
  此浏览器不支持canvas
</canvas>

    2、获取webgl上下文

<script>
  const ctx1 = document.getElementById('canvas');
  const gl = ctx.getContext('webgl');
</script>

    3、设置画布颜色

<script>
  const ctx1 = document.getElementById('canvas');
  const gl = ctx.getContext('webgl');
  gl.clearColor(1.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
</script>

WebGL+Three.js—第一章 WebGL简单应用

1.3.3 设置与清空背景色

    1、gl.clearColor(r,g,b,a)

WebGL+Three.js—第一章 WebGL简单应用

        如果任何值小于0或者大于1,那么就会被截断为0.0或者1.0

        示例程序执行gl.clearColor(1.0, 0.0, 0.0, 1.0),背景色就被指定为了红色。

        我们一般指定颜色时,颜色的值是0到255之间的,但是由于webgl是继承自OpenGL,所以它遵循传统OpenGL颜色分量的取值范围。

        一旦指定的背景色,背景色就会驻存在WegGL系统中,在下一次调用gl.clearColor之前都不会改变。如果你将来还想用同一个颜色清空绘图区,就没必须要再指定一次背景色。

    2、gl.clear(buffer)

        函数参数是gl.COLOR_BUFFER_BIT,这是因为WebGL中,gl.clear()方法实际上继承自OpenGL,它基于多基本缓冲区模型。这比二维绘图上下文复杂的多。清空绘图区域,实际上在清空颜色缓冲区,传递参数gl.COLOR_BUFFER_BIT就是告诉WebGL清空颜色缓冲区。除了颜色缓冲区,WebGL还有其他种类的缓冲区,比如深度缓冲区和模板缓冲区。

        gl.clear(buffer)的用法:buffer是指定待清空的颜色缓冲区,位操作符OR(|)可用于指定多个缓冲区

WebGL+Three.js—第一章 WebGL简单应用

        (1)gl.clear(gl.COLOR_BUFFER_BIT):对应的设置方法为gl.clearColor(0.0, 0.0, 0.0, 1.0)

        (2)gl.clear(gl.DEPTH_BUFFER_BIT):对应的设置方法为gl.clearDepth(1.0)

        (3)gl.clear(gl.STENCIL_BUFFER_BIT):对应的设置方法为gl.clearStencil(0)

1.3.4 代码示例

1.4 绘制一个点

WebGL+Three.js—第一章 WebGL简单应用

1.4.1 着色器概念

    着色器就是让开发者自己去编写一段程序,用来代替固定渲染管线,来处理图像的渲染。

    1、顶点着色器

        顶点着色器用来描述顶点的特性,通过计算获取位置信息。顶点是指二维三维空间中的一个点,可以理解为一个个坐标。

    2、片元着色器

        片元着色器进行逐片处理程序,通过计算获取颜色信息。片元可以理解为一个个像素。

    3、着色器工作流程

        我们的着色器程序,其实是以字符串的形式存放在JavaScript里面,它的源程序其实是通过JavaScript字符串来完成的。JavaScript读取到着色器的信息,传递给webgl,webgl通过一系列的操作抓取到着色器的内容,计算出顶点和片元信息,之后通过绘制程序绘制到浏览器上。

WebGL+Three.js—第一章 WebGL简单应用

1.4.2 创建着色器源码

    1、创建着色器源码

<script>
  const ctx1 = document.getElementById('webgl');
  const gl = ctx1.getContext('webgl');

  /**
   * 创建着色器源码
   * */
  // 顶点着色器
  // gl_Position vec4(0.0, 0.0, 0.0, 1.0) x, y, z, w齐次坐标(x/w, y/w, z/w)
  const VERTEX_SHADER_SOURCE = `
    // 必须要存在 main 函数
    void main() {
      // 要绘制的点的坐标
      gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
      // 点的大小
      gl_PointSize = 30.0;
    }
  `;

  // 片元着色器
  // gl_FragColor vec4(1.0, 0.0, 0.0, 1.0) r, b, g, a
  const FRAGMENT_SHADER_SOURCE = `
    void main() {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
    }
  `;
</script>

        void是指函数没有返回值,main是主函数,必须要存在。gl_Position和gl_PointSize分别是点坐标和点的大小,gl_FragColor指定颜色。注意PointSize是浮点类型。

    2、vec4()

        在gl_Position使用vec4(0.0, 0.0, 0.0, 1.0)。这里的vec4的4个参数分别为x,y,z,w齐次坐标(x/w, y/w, z/w)

        在gl_FragColor使用vec4(1.0, 0.0, 0.0, 1.0)。这里的vec4的4个参数分别为r,g,b,a。

1.4.3 创建着色器

    1、创建顶点和片元着色器

        通过gl.createShader方法创建顶点和片元着色器。

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

    2、关联着色器源码

        通过gl.shaderSource方法将着色器与源码关联起来。

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE);        // 指定顶点着色器的源码
gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE);    // 指定片元着色器的源码

    3、编译着色器

        通过gl.compileShader方法进行编译着色器。

const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE);        // 指定顶点着色器的源码
gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE);    // 指定片元着色器的源码

// 编译着色器
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);

1.4.4 创建程序对象

    程序对象的作用是关联JavaScript和着色器的,之后的功能都是通过程序对象来实现的,接下来需要对程序对象指定对应的着色器。

    1、创建程序对象

// 创建一个程序对象
const program = gl.createProgram();

    2、程序对象指定着色器

// 创建一个程序对象
const program = gl.createProgram();

gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);

    3、关联程序对象

gl.linkProgram(program);

    4、使用程序对象

gl.useProgram(program);

1.4.5 执行绘制

    可以通过gl.drawArrays方法进行绘制,该方法接收3个参数:绘制的图形,开始位置,顶点个数。

gl.drawArrays(gl.POINTS, 0, 1);

WebGL+Three.js—第一章 WebGL简单应用

    gl.drawArrays方法除了绘制点,还可以绘制很多其他的图形,例如线段、三角形等等。

1.4.6 封装着色器流程

    从创建着色器到使用程序对象,这套流程是属于webgl固定不变的流程,可以进行抽取封装。

function initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE) {
  const vertexShader = gl.createShader(gl.VERTEX_SHADER);
  const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);

  gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE);        // 指定顶点着色器的源码
  gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE);    // 指定片元着色器的源码

  // 编译着色器
  gl.compileShader(vertexShader);
  gl.compileShader(fragmentShader);

  // 创建一个程序对象
  const program = gl.createProgram();

  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);

  gl.linkProgram(program);

  gl.useProgram(program);

  return program;
}

1.4.7 代码示例

1.5 介绍webgl三维坐标系

1.5.1 canvas坐标系

    canvas原点的位置在画布的左上角,往右是x轴的正方向,往下是y轴的正方向。

WebGL+Three.js—第一章 WebGL简单应用

1.5.2 webgl坐标系

    1、坐标系概念

        webgl坐标系的水平方向为x轴的正方向,往上是y轴的正方向,垂直于平面往外为z轴的正方向。

WebGL+Three.js—第一章 WebGL简单应用

    2、绘图区域

        webgl坐标系的水平方向为x轴的正方向,往上是y轴的正方向,垂直于平面往外为z轴的正方向。

WebGL+Three.js—第一章 WebGL简单应用

1.5.3 右手坐标系与左手坐标系

    左手与右手的区别在于z轴的方向,右手的z轴方向是从屏幕里指向自己,左手的z轴方向是自己视线的方向。

WebGL+Three.js—第一章 WebGL简单应用

1.6 学习使用attribute变量

const VERTEX\_SHADER\_SOURCE = `         // 必须要存在 main 函数
  void main() {
    // 要绘制的点的坐标
    gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
    // 点的大小
    gl_PointSize = 30.0;
  }
`;

    目前的点信息是直接写在了源程序里面的,如果想动态去改变点信息,除非去改源程序,这样的做法显然不合理。

    webgl提供了attribute变量,解决了这个动态改变源程序信息的问题。

1.6.1 流程

WebGL+Three.js—第一章 WebGL简单应用

1.6.2 声明变量

    attribute变量的使用类似于js中的var或者let,它声明的位置是在main函数的外面。

const VERTEX_SHADER_SOURCE = `
  attribute vec4 aPosition;
  void main() {
    // 要绘制的点的坐标
    gl_Position = aPosition;  // vec4(0.0,0.0,0.0,1.0)
    // 点的大小
    gl_PointSize = 30.0;
  }
`;

WebGL+Three.js—第一章 WebGL简单应用
    
    着色器是强类型语言,attribute定义的类型要跟实际接收的类型要一致,例如gl_Position它接收的就是vec4的类型。

WebGL+Three.js—第一章 WebGL简单应用

    这里的aPosition变量并没有赋值,但它仍然能显示,是因为它自动给aPosition赋给了点的默认值vec4(0.0,0.0,0.0,1.0)。

    注意:attribute变量只能在顶点着色器中使用,不能在片元着色器中使用。

1.6.3 获取attribute变量

    我们可以通过gl.getAttribLocation(program, name)方法获取attribute的变量。program是程序对象,name是attribute的变量名。它会返回变量的存储地址。

const aPosition = gl.getAttribLocation(program, 'aPosition');

    注意:获取attribute变量需要在initShader函数之后,因为会用到program这个程序对象。

1.6.4 给attribute变量赋值

    attribute的赋值形式,不能像js直接aPosition = 1这样赋值,需要使用webgl提供的方法。

    1、gl.vertexAttrib4f(location, v1, v2, v3, v4)

WebGL+Three.js—第一章 WebGL简单应用

const aPosition = gl.getAttribLocation(program, 'aPosition');

gl.vertexAttrib4f(aPosition, 0.5, 0.5, 0.0, 1.0);

WebGL+Three.js—第一章 WebGL简单应用

    2、gl.vertexAttrib4f同族函数介绍

WebGL+Three.js—第一章 WebGL简单应用

gl.vertexAttrib3f(aPosition, 0.5, 0.5, 0.0);
gl.vertexAttrib2f(aPosition, 0.5, 0.5);
gl.vertexAttrib1f(aPosition, 0.5);

    注意:3f、2f、1f这些方法,可以理解为4f方法的简略版,它会在省略的参数补上一个默认值,例如gl.vertexAttrib3f(aPosition, 0.5, 0.5, 0.0),它就相当于gl.vertexAttrib4f(aPosition, 0.5, 0.5, 0.0, 1.0),3f它会帮我们补上第4个参数的默认值1.0,其余的2f和1f方法也以此类推。

1.6.5 通过js修改attribute变量

    使用定时器每隔200ms移动一次点的位置。

let x = 0;
setInterval(() => {
  x += 0.1;
  if (x > 1.0) {
    x = 0;
  }
  gl.vertexAttrib1f(aPosition, x);
  gl.drawArrays(gl.POINTS, 0, 1);
}, 200);

    注意:每次修改完之后,都需要重新执行绘制函数。

1.6.6 代码示例

1.7 通过鼠标控制绘制

1.7.1 流程

WebGL+Three.js—第一章 WebGL简单应用

1.7.2 添加点击事件

    webgl没有办法监听点击事件,因此点击事件还是要添加到画布上。

ctx.onclick = function() {}

1.7.3 获取点击位置

    如果想通过鼠标点击来绘制点,就要先获取绘制的点坐标。

    1、获取鼠标当前坐标

        我们可以通过事件的event对象的clientX和clientY来获取当前鼠标位于屏幕的x和y坐标。

ctx.onclick = function(event) {
  // 坐标
  const x = event.clientX;
  const y = event.clientY;
}

    2、获取canvas与屏幕的左方和上方的距离

WebGL+Three.js—第一章 WebGL简单应用

        想要在canvas里点击绘制,除了获取鼠标当前坐标还不够,还需要减去canvas与屏幕的左方和上方的距离,这样才能真正获取到鼠标点击的位置相对于canvas的偏移位置。

        这里可以通过ctx.offsetLeft和ctx.offsetTop来获取canvas与屏幕的左方和上方的距离。除此之外,还可以使用event.target.getBoundingClientRect()方法来获取。

ctx.onclick = function(event) {
  const domPosition = event.target.getBoundingClientRect()
  console.log(domPosition)
}

WebGL+Three.js—第一章 WebGL简单应用

    3、获取点击位置与canvas的相对位置

        目前已知鼠标的坐标和canvas与屏幕的距离,只需要用坐标减去距离即可。

ctx.onclick = function(event) {
  // 坐标
  const x = event.clientX;
  const y = event.clientY;

  const domPosition = event.target.getBoundingClientRect();

  const domx = x - domPosition.left;
  const domy = y - domPosition.top;
}

    4、转换webgl间距算法

        webgl从左到右的取值范围是-1到1,webgl从上往下的取值范围是1到-1。而目前我们得到的是一个真实的相对位置,因此需要将这个位置的横向和纵向的位置转换成对应的区间。

        (1)横向转换

            目前画布的width为400,那么最左侧为0,中心点为200,最右侧为400,区间是0-400。想转换成-1到1的区间,只需要把canvas往左移动canvas一半的宽度即可。移动之后,最左侧为-200,中心点为0,最右侧为200。最后统一除以canvas一半的宽度,那就转换成-1到1的区间了。

            公式:webgl的x位置 = (相对x位置 – 200) / 200

        (2)纵向转换

            目前画布的height为400,那么最上侧为0,中心点为200,最下侧为400,区间是0-400。想转换成1到-1的区间,由于方向刚好相反,相对位置往下是正方向,而webgl往上才是正方向,因此需要使用canvas一半的高度减去相对位置,再除以canvas一半的高度,那就转换成1到-1的区间了。

            公式:webgl的y位置 = (200 – 相对y位置) / 200

1.7.4 鼠标控制绘制

    1、点击绘制

ctx.onclick = function(event) {
  // 坐标
  const x = event.clientX;
  const y = event.clientY;

  const domPosition = event.target.getBoundingClientRect();

  const domx = x - domPosition.left;                // 获取鼠标在画布的水平方向的相对位置
  const domy = y - domPosition.top;                 // 获取鼠标在画布的垂直方向的相对位置

  const halfWidth = ctx.offsetWidth / 2;            // 画布一半的宽度
  const halfHeight = ctx.offsetHeight / 2;          // 画布一半的高度

  const clickX = (domx - halfWidth) / halfWidth;    // 转换成webgl水平方向的-1和1
  const clickY = (halfHeight - domy) / halfHeight;  // 转换成webgl垂直方向的1和-1

  gl.vertexAttrib2f(aPosition, clickX, clickY);
  gl.drawArrays(gl.POINTS, 0, 1);
}

WebGL+Three.js—第一章 WebGL简单应用

    2、移动跟随绘制

ctx.onmousemove = function(event) {
  // 坐标
  const x = event.clientX;
  const y = event.clientY;

  const domPosition = event.target.getBoundingClientRect();

  const domx = x - domPosition.left;                // 获取鼠标在画布的水平方向的相对位置
  const domy = y - domPosition.top;                 // 获取鼠标在画布的垂直方向的相对位置

  const halfWidth = ctx.offsetWidth / 2;            // 画布一半的宽度
  const halfHeight = ctx.offsetHeight / 2;          // 画布一半的高度

  const clickX = (domx - halfWidth) / halfWidth;    // 转换成webgl水平方向的-1和1
  const clickY = (halfHeight - domy) / halfHeight;  // 转换成webgl垂直方向的1和-1

  gl.vertexAttrib2f(aPosition, clickX, clickY);
  gl.drawArrays(gl.POINTS, 0, 1);
}

WebGL+Three.js—第一章 WebGL简单应用

    3、画笔效果绘制

const points = [];
ctx.onmousemove = function(event) {
  // 坐标
  const x = event.clientX;
  const y = event.clientY;

  const domPosition = event.target.getBoundingClientRect();

  const domx = x - domPosition.left;                // 获取鼠标在画布的水平方向的相对位置
  const domy = y - domPosition.top;                 // 获取鼠标在画布的垂直方向的相对位置

  const halfWidth = ctx.offsetWidth / 2;            // 画布一半的宽度
  const halfHeight = ctx.offsetHeight / 2;          // 画布一半的高度

  const clickX = (domx - halfWidth) / halfWidth;    // 转换成webgl水平方向的-1和1
  const clickY = (halfHeight - domy) / halfHeight;  // 转换成webgl垂直方向的1和-1

  // gl.vertexAttrib2f(aPosition, clickX, clickY);
  // gl.drawArrays(gl.POINTS, 0, 1);

  points.push({
    clickX, clickY
  });

  for(let i=0; i < points.length; i++) {
    gl.vertexAttrib2f(aPosition, points[i].clickX, points[i].clickY);
    gl.drawArrays(gl.POINTS, 0, 1);
  }
}

WebGL+Three.js—第一章 WebGL简单应用

1.7.5 代码示例

1.8 使用uniform变量

    在顶点着色器中,我们可以通过attribute变量进行修改里面的点信息。在片元着色器里,我们可以通过uniform变量改变里面的颜色。

1.8.1 流程

WebGL+Three.js—第一章 WebGL简单应用

1.8.2 声明变量

    uniform变量与attribute变量类似,它也是声明在main函数的外面。

const FRAGMENT_SHADER_SOURCE = `
  uniform vec4 uColor;
  void main() {
    gl_FragColor = uColor;    // vec4(0.0, 0.0, 0.0, 1.0);
  }
`;

1.8.3 获取uniform变量

    我们可以通过gl.getUniformLocation(program, name)方法获取uniform的变量。program是程序对象,name是uniform的变量名。它会返回变量的存储地址。

const uColor = gl.getUniformLocation(program, 'uColor');

1.8.4 给uniform变量赋值

    1、gl.uniform4f(name, v1, v2, v3, v4)

const uColor = gl.getUniformLocation(program, 'uColor');

gl.uniform4f(uColor, 1.0, 0.0, 0.0, 1.0);

    2、gl.uniform4f同族函数介绍

WebGL+Three.js—第一章 WebGL简单应用

        这里的1f、2f、3f的使用方法,跟vertexAttrib系列有点区别。uniform的方法使用,需要匹配类型,例如vec4类型就需要使用uniform4f,vec3就使用uniform3f。

        由于gl_FragColor需要接收的是vec4的类型,如果定义用的是vec2或者vec3等,可以使用vec4()函数进行转换,函数里把对应的参数根据rgba的顺序读取即可。

const FRAGMENT_SHADER_SOURCE = `
  precision mediump float;
  uniform vec3 uColor;
  void main() {
    gl_FragColor = vec4(uColor.r, uColor.g, uColor.b, 1.0);    // vec4(0.0, 0.0, 0.0, 1.0);
  }
`;
gl.uniform3f(uColor, 1.0, 0.0, 0.0);

        注意:2f的方法匹配vec2类型,跟vec3一样,但是1f的方法是例外,它对应的类型不是vec1,而是float,而且使用vec4()函数转的时候,直接使用uColor变量,后面补上gba3个参数。

// 片元着色器
const FRAGMENT_SHADER_SOURCE = `
  precision mediump float;
  uniform float uColor;
  void main() {
    gl_FragColor = vec4(uColor, 0.0, 0.0, 1.0);
  }
`;
gl.uniform1f(uColor, 1.0);

1.8.5 设置精度

    在片元着色器中如果想使用矢量或者浮点数的时候,需要先设置它的精度。在顶点着色器使用矢量的时候,它会指定默认的精度为高精度,但是在片元着色器并没有指定它的默认精度,需要自己手动设置。

    可以通过precision mediump float;进行设置,它的意思是指定精度为中精度。它们的精度值分别为:

    (1)高精度:highp

    (2)中精度:mediump

    (3)低精度:lowp

const FRAGMENT_SHADER_SOURCE = `
  precision mediump float;
  uniform vec4 uColor;
  void main() {
    gl_FragColor = uColor;    // vec4(0.0, 0.0, 0.0, 1.0);
  }
`;

1.8.6 顶点着色器使用uniform变量

    uniform变量和attribute变量不同,它既可以用在片元着色器,也可以用在顶点着色器里。

const VERTEX_SHADER_SOURCE = `
  uniform vec4 uPosition;
  attribute vec4 aPosition;
  void main() {
    // 要绘制的点的坐标
    gl_Position = aPosition;    // vec4(0.0,0.0,0.0,1.0)
    // 点的大小
    gl_PointSize = 10.0;
  }
`;

    注意:uniform变量不能传递顶点数据,也就是说gl_Position不能接收uniform变量。这是因为顶点的数据每一个的坐标都不一样,而uniform的值是固定的,这时候就不能将统一的数据传到每个不同的顶点上。

1.8.7 绘制不同颜色的点

    在每次点击鼠标绘制点的时候,使用gl.uniform2f()方法把x和y坐标作为参数传入进去,即可实现点击绘制不同颜色的点。

const FRAGMENT_SHADER_SOURCE = `
  precision mediump float;
  uniform vec2 uColor;
  void main() {
    gl_FragColor = vec4(uColor.r, uColor.g, 0.0, 1.0);
  }
`;

/**
 * 创建着色器
 * */
const program = initShader(gl, VERTEX_SHADER_SOURCE, FRAGMENT_SHADER_SOURCE);

const aPosition = gl.getAttribLocation(program, 'aPosition');
const uColor = gl.getUniformLocation(program, 'uColor');

const points = [];
ctx.onclick = function(event) {
  // 坐标
  const x = event.clientX;
  const y = event.clientY;

  const domPosition = event.target.getBoundingClientRect();

  const domx = x - domPosition.left;                // 获取鼠标在画布的水平方向的相对位置
  const domy = y - domPosition.top;                 // 获取鼠标在画布的垂直方向的相对位置

  const halfWidth = ctx.offsetWidth / 2;            // 画布一半的宽度
  const halfHeight = ctx.offsetHeight / 2;          // 画布一半的高度

  const clickX = (domx - halfWidth) / halfWidth;    // 转换成webgl水平方向的-1和1
  const clickY = (halfHeight - domy) / halfHeight;  // 转换成webgl垂直方向的1和-1

  points.push({
    clickX, clickY
  });

  for(let i=0; i < points.length; i++) {
    gl.vertexAttrib2f(aPosition, points[i].clickX, points[i].clickY);
    gl.uniform2f(uColor, points[i].clickX, points[i].clickY);
    gl.drawArrays(gl.POINTS, 0, 1);
  }
}

WebGL+Three.js—第一章 WebGL简单应用

1.8.8 代码示例

原文链接:https://juejin.cn/post/7245682364932554808 作者:sowhat88

(0)
上一篇 2023年6月20日 上午11:08
下一篇 2023年6月20日 上午11:18

相关推荐

发表回复

登录后才能评论