WebGL编程指南-29 通过鼠标点击选中立方体,选中立方体的某个面

1.三维物体选中原理与实现步骤

选中三维物体的原理就是先将几何体设置为唯一的颜色,也就是说场景中的几何体颜色都不同,我们都知道gl_Color的类型是vec4类型的,也就是占4个字节,那么有256256256*256种可能,也可以理解位颜色就是各个几何体的ID,当鼠标点击屏幕时取当前位置的像素颜色,然后根据颜色找到对应几何体。最后再还原几何体本身的颜色。

2.demo效果

在这里插入图片描述
如上图当我们点击立方体时,会alert出提示。

3.demo代码

const VSHADER_SOURCE = `
attribute vec4 a_Position;

uniform vec4 u_Color;
varying vec4 v_Color;
uniform mat4 u_MvpMatrix4;
uniform bool u_Clicked;
void main() {
    gl_Position=u_MvpMatrix4*a_Position;
    if(u_Clicked) {
        v_Color = vec4(1.0,0.0,0.0,1.0);
    }else {
        v_Color=u_Color;

    }
  
}

`;

const FSHADER_SOURCE = `
precision mediump float;
varying vec4 v_Color;

void main() {
    gl_FragColor=v_Color;
}
`;

const initArrayBuffer = (gl, data, name, num, type) => {
  const buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

  const vertexLocation = gl.getAttribLocation(gl.program, name);
  gl.vertexAttribPointer(vertexLocation, num, type, false, 0, 0);
  gl.enableVertexAttribArray(vertexLocation);

  return true;
};

const check = (
  gl,
  n,
  x,
  y,
  currentAngle,
  u_Clicked,
  viewProjMatrix,
  u_MvpMatrix4
) => {
  let picked = false;
  gl.uniform1i(u_Clicked, 1);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.drawElements(gl.TRIANGLES, 24, gl.UNSIGNED_BYTE, 0);
 
  const pixels = new Uint8Array(4);
  gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

  if(pixels[0] ===255) {
      picked = true
  }
  gl.uniform1i(u_Clicked,0);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.drawElements(gl.TRIANGLES, 24, gl.UNSIGNED_BYTE, 0);

  return picked
};
const initIndexBuffer = (gl, indexData) => {
  const indicesBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);
};
const main = () => {
  const canvas = document.getElementById("webgl");
  const gl = canvas.getContext("webgl");

  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    return console.error("着色器初始化失败");
  }

  canvas.onmousedown = (ev) => {
    const x = ev.clientX,
      y = ev.clientY;
    const rect = ev.target.getBoundingClientRect();

    if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {
        const x_in_canvas=x-rect.left;
        const y_in_canvas=rect.bottom-y;
        const u_Clicked = gl.getUniformLocation(gl.program,'u_Clicked')
        const picked = check(gl,0,x_in_canvas,y_in_canvas,0,u_Clicked);
        if(picked) {
            alert('the cube was selected!')
        }
    }
  };
  const modelMatrix4 = new Matrix4().setRotate(60, 0, 1, 0);
  const projMatrix4 = new Matrix4().setPerspective(
    90,
    canvas.width / canvas.clientHeight,
    0.5,
    100
  );
  const viewMatrix4 = new Matrix4().setLookAt(
    3,
    3,
    5,
    0.0,
    0.0,
    0.0,
    0.0,
    1.0,
    0.0
  );

  const mvpMatrix4 = new Matrix4()
    .setIdentity()
    .multiply(projMatrix4)
    .multiply(viewMatrix4)
    .multiply(modelMatrix4);

  const u_MvpMatrix4 = gl.getUniformLocation(gl.program, "u_MvpMatrix4");
  gl.uniformMatrix4fv(u_MvpMatrix4, false, mvpMatrix4.elements);
  const vertexBuffer = new Float32Array([
    1.0,
    1.0,
    1.0, //v0 white
    -1.0,
    1.0,
    1.0, //v1 品红
    -1.0,
    -1.0,
    1.0, //v2 红色
    1.0,
    -1.0,
    1.0, //v3 黄色
    1.0,
    -1.0,
    -1.0, //v4
    1.0,
    1.0,
    -1.0, //v5
    -1.0,
    1.0,
    -1.0, //v6
    -1.0,
    -1.0,
    -1.0, //v7
  ]);
  const indicesData = new Uint8Array([
    0,
    1,
    2,
    0,
    2,
    3, //front
    0,
    3,
    4,
    0,
    4,
    5, //right
    0,
    5,
    6,
    0,
    6,
    1, //up
    1,
    6,
    7,
    1,
    7,
    2, //left
    7,
    4,
    3,
    7,
    3,
    2, //bottom
    4,
    7,
    6,
    4,
    6,
    5, //behind
  ]);
  initArrayBuffer(gl, vertexBuffer, "a_Position", 3, gl.FLOAT);
  initIndexBuffer(gl, indicesData);

  const u_Clicked = gl.getUniformLocation(gl.program, "u_Clicked");
  //初始化为false
  gl.uniform1i(u_Clicked, 0);
  const u_Color = gl.getUniformLocation(gl.program, "u_Color");
  gl.uniform4f(u_Color, 0.0, 1.0, 0.0, 1.0);

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.drawElements(gl.TRIANGLES, indicesData.length, gl.UNSIGNED_BYTE, 0);
};

main();

4.相关API介绍

以上代码有一个gl.readPixels() 用来读取指定区域的像素颜色

调用示例:gl.readPixels(x, y, width, height, format, type, pixels)
--------------------------------------------------------------------------
函数功能:从颜色缓冲区中读取由x,y,width,height参数确定的矩形中的所有像素值,
		 并保存在pixels指定的数组中
--------------------------------------------------------------------------			
参数		
			x, y				指定颜色缓冲区中矩形块左上角的坐标
			width, height		指定矩形的宽度和高度,以像素为单位
			format				指定像素值的颜色格式,必须为gl.RGBA
			type				指定像素值的数据格式,必须为gl.UNSIGNED_BYTE
			pixels				指定用来接收像素数据的Uint8Array类型化数组

--------------------------------------------------------------------------		
返回值		无
--------------------------------------------------------------------------
错误		INVALID_VALUE		pixels为null,或者,width或height是负值
			INVALID_OPERATION	pexels的长度不够存储所有像素值数据
			INVALID_ENUM		format或type值无效

4.鼠标点击选中立方体一个表面

4.1demo效果

在这里插入图片描述
如上图所示,若点击立方体的一个面,则这个面变成白色,点击立方体以外的区域,立方体恢复原有颜色

逻辑与选中立方体逻辑基本一致,只不过之前颜色相当于立方体的ID,现在颜色相当于面的ID,每个面的颜色不同

4.2 demo代码

const VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute float a_Face;

uniform vec4 u_Color;
varying vec4 v_Color;
uniform mat4 u_MvpMatrix4;
uniform bool u_Clicked;
uniform int u_PickedFace;

void main() {
    gl_Position=u_MvpMatrix4*a_Position;
    int face = int(a_Face);
    vec3 color = (face == u_PickedFace)?vec3(1.0):u_Color.rgb;
    if(u_PickedFace == 0) {
        v_Color = vec4(color,a_Face/255.0);
    }else {
        v_Color=vec4(color,u_Color.a);
    }
  
}

`;

const FSHADER_SOURCE = `
precision mediump float;
varying vec4 v_Color;

void main() {
    gl_FragColor=v_Color;
}
`;

const initArrayBuffer = (gl, data, name, num, type) => {
  const buffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
  gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);

  const vertexLocation = gl.getAttribLocation(gl.program, name);
  gl.vertexAttribPointer(vertexLocation, num, type, false, 0, 0);
  gl.enableVertexAttribArray(vertexLocation);

  return true;
};

const check = (
  gl,
  n,
  x,
  y,
  currentAngle,
  u_PickedFace,
  viewProjMatrix,
  u_MvpMatrix4
) => {
    const pixels = new Uint8Array(4);
  gl.uniform1i(u_PickedFace,0);//将表面编号写入分量

  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.drawElements(gl.TRIANGLES, 24, gl.UNSIGNED_BYTE, 0);
 
  gl.readPixels(x, y, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
  return pixels[3]
};
const initIndexBuffer = (gl, indexData) => {
  const indicesBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);
};
const main = () => {
  const canvas = document.getElementById("webgl");
  const gl = canvas.getContext("webgl");
  
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
    return console.error("着色器初始化失败");
  }

  canvas.onmousedown = (ev) => {
    const x = ev.clientX,
      y = ev.clientY;
    const rect = ev.target.getBoundingClientRect();

    if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {
        const x_in_canvas=x-rect.left;
        const y_in_canvas=rect.bottom-y;
        const u_PickedFace = gl.getUniformLocation(gl.program,'u_PickedFace')
        const face = check(gl,0,x_in_canvas,y_in_canvas,0,u_PickedFace);
        console.log(face,face)
        gl.uniform1i(u_PickedFace,face);

        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        gl.drawElements(gl.TRIANGLES, 24, gl.UNSIGNED_BYTE, 0);
    }
  };

  const u_PickedFace = gl.getUniformLocation(gl.program,'u_PickedFace');
  gl.uniform1i(u_PickedFace,-1);

  const modelMatrix4 = new Matrix4().setRotate(60, 0, 1, 0);
  const projMatrix4 = new Matrix4().setPerspective(
    90,
    canvas.width / canvas.clientHeight,
    0.5,
    100
  );
  const viewMatrix4 = new Matrix4().setLookAt(
    3,
    3,
    5,
    0.0,
    0.0,
    0.0,
    0.0,
    1.0,
    0.0
  );

  const mvpMatrix4 = new Matrix4()
    .setIdentity()
    .multiply(projMatrix4)
    .multiply(viewMatrix4)
    .multiply(modelMatrix4);

  const u_MvpMatrix4 = gl.getUniformLocation(gl.program, "u_MvpMatrix4");
  gl.uniformMatrix4fv(u_MvpMatrix4, false, mvpMatrix4.elements);
  const vertexBuffer =new Float32Array([
    1.0,
    1.0,
    1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    1.0, //front面 v0-4
    1.0,
    1.0,
    1.0,
    1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    1.0,
    -1.0, //right v0345
    1.0,
    1.0,
    1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    1.0, //up v0561
    -1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    -1.0,
    -1.0,
    -1.0,
    -1.0,
    1.0,
    -1.0, //left
    -1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    -1.0,
    -1.0,
    -1.0, //down
    1.0,
    -1.0,
    -1.0,
    1.0,
    1.0,
    -1.0,
    -1.0,
    1.0,
    -1.0,
    -1.0,
    -1.0,
    -1.0, //back
  ]);
  const indicesData =new Uint8Array([
    0, 1, 2, 0, 2, 3, 4, 5, 6, 4, 6, 7, 8, 9, 10, 8, 10, 11, 12, 13, 14, 12, 14,
    15, 16, 17, 18, 16, 18, 19, 20, 21, 22, 20, 22, 23,
  ]);
  const faces = new Uint8Array([
      1,1,1,1,
      2,2,2,2,
      3,3,3,3,
      4,4,4,4,
      5,5,5,5,
      6,6,6,6
  ])
  initArrayBuffer(gl,faces,"a_Face",1,gl.BYTE)
  initArrayBuffer(gl, vertexBuffer, "a_Position", 3, gl.FLOAT);
  initIndexBuffer(gl, indicesData);

  const u_Clicked = gl.getUniformLocation(gl.program, "u_Clicked");
  //初始化为false
  gl.uniform1i(u_Clicked, 0);
  const u_Color = gl.getUniformLocation(gl.program, "u_Color");
  gl.uniform4f(u_Color, 0.0, 1.0, 0.0, 1.0);

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.enable(gl.DEPTH_TEST);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  gl.drawElements(gl.TRIANGLES, indicesData.length, gl.UNSIGNED_BYTE, 0);
};

main();

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 《WebGL编程指南》是一本介绍WebGL技术的经典书籍,它详细阐述了WebGL的原理、应用和实践,是WebGL开发必备的参考书。本书附带的源代码是学习和实践WebGL的重要材料,它涵盖了WebGL的各个方,从基础的三维图形绘制到高级的光影渲染和复杂的交互效果。 源代码包含了多个示例程序,各个示例程序围绕着一个主题展开。例如,有些示例程序演示了如何绘制简单的三维图形,如立方体和球体,还有些示例程序演示了如何添加纹理和材质,以及如何实现跨浏览器的兼容性。 此外,本书中的源代码还包括了一些高级的示例程序,如阴影、反射和抗锯齿等。这些示例程序可以帮助读者深入理解WebGL的工作原理和实现方法,并在实践中掌握WebGL技术。 值得一提的是,本书的源代码是经过精心设计和优化的,它压缩和合并了多个文件,以提高程序的性能和加载速度。因此,读者在使用本书源代码时,需要按照书中指导的步骤进行安装和部署,以获得最佳的开发体验。 总之,本书的源代码是WebGL学习和实践的重要资源,它提供了丰富的示例程序和实战经验,帮助读者快速掌握WebGL技术,开发出优秀的WebGL应用程序。 ### 回答2: WebGL编程指南是一本关于WebGL技术的教材,涵盖了从基础知识到高级应用的内容。其中提供了多个实例,结合源代码,帮助读者快速掌握WebGL编程技巧。 这本书的源代码非常有用,通过它可以理解WebGL的原理,并且快速搭建自己的WebGL应用程序。源代码中详细说明了各个组件的作用和用法,还提供了多种渲染器、着色器和纹理的实现方法,这些都对于初学者来说非常有价值。 WebGL编程指南源代码重点涵盖了三个方:顶点缓冲对象、着色器和纹理。在顶点缓冲对象方,它提供了多种创建和操作缓冲对象的实现方法,包括创建缓冲区、填充缓冲区、更新缓冲区等。在着色器方,源代码中提供了包括简单着色器、光照着色器、Phong着色器在内的多种实现方法,这些着色器实现了不同的效果,读者可以根据需要自行选择。 最后是纹理,源代码提供了多种纹理类的实现方法,支持2D纹理、CubeMap纹理,以及多种纹理过滤和纹理映射方式。 总之,WebGL编程指南源代码提供了非常有价值的WebGL编程实践经验,对于从事WebGL开发的人员,特别是初学者,这是一份非常有意义的参考资料。 ### 回答3: 《WebGL编程指南》的源码是一本非常有用的资源,可以帮助读者更深入地理解WebGL编程技术。这本书的源码包括了许多有趣、实用的示例,可以帮助读者学会如何使用WebGL进行3D图形编程。 在掌握了基本的WebGL编程知识后,读者可以通过学习这些示例来深入了解WebGL技术,例如如何使用各种着色器、如何创建和渲染各种3D对象、如何进行照明和纹理映射等等。 同时,《WebGL编程指南》的源码也很容易上手。每个示例都有详细的注释和说明,使得读者可以很容易地理解代码的实现细节。读者可以将这些例子作为起点,自行修改和扩展,创造出更加有趣、独特的WebGL应用。 总之,《WebGL编程指南》的源码是非常有价值的,可以帮助广大编程爱好者、WebGL初学者甚至是资深的WebGL开发者,掌握更加深入的WebGL编程技术,为他们创造出更加精彩、创新的WebGL应用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值