three.js的着色器(巨详细 初学者 大白话)

three.js就不过多介绍了 可以看另一篇文章 总结就是场景  相机  和 渲染器

学起来 也比较轻松

后来看到了着色器 给我整懵乐了  一会一个API  一会一个API 都没见过 然后就一点点去学习 真的是费了好大劲了 需要知道很多新东西 才能初步知道和使用着色器

当然如果只是简单的使用着色器  比如就画一个点  加个颜色还是很容易的

我想做的是一个几个顶点确定一个平面 并且颜色是渐变色的

(之前还好奇渐变色是怎么做的 就是 顶点间的颜色不同 两个顶点间就会有过渡色了)

上最后效果图

要想知道着色器的使用 确实需要了解一下webgl 因为是用它的底层实现的 不然很多基本的概念很模糊

webgl 其实就和canvas差不多 都是用来绘画的  

他的用法 例子  推荐一篇文章 初学者很适合

【WebGL】简单入门教程_小沈曰的博客-CSDN博客

建议看一遍 然后 我再用大白话解释一遍 以及总结一下 不然初学者可能会问 为什么使用这个API

1首先要想使用webgl 就要有canvs画布

 2 获取到画布元素的基于webgl上下文环境对象  可以把这个当做webgl(可能概念不是那么准确不较真的话是可以这么认为的)

3  一个webgl 最少两个着色器 当然我也只是试了两个着色器  一个是顶点着色器 一个是片段着色器

 顶点着色器 是描述顶点的  片段着色器是用来描述颜色的 (当前我是这么用的)

如果涉及到了变量 需要注意的是 都是将变量传递到顶点着色器  再在着色器内 赋值给两个共用的变量

里面的变量声明 有三个标识  我反正没纠结他 就当做 public  private这种表明一下是什么变量吧

attribute,varying 这种

 var vertShader = gl.createShader(gl.VERTEX_SHADER)

    var fragShader = gl.createShader(gl.FRAGMENT_SHADER)

4 声明好两个着色器以后 就要把代码段传进去

 gl.shaderSource(fragShader, FSHADER_SOURCE)

   gl.shaderSource(vertShader, VSHADER_SOURCE)

5 传进去后 就要编译代码段 类似执行里面的预言main函数

    gl.compileShader(fragShader)

    gl.compileShader(vertShader)

6任何的着色器都需要一个启动程序

 创建启动程序  然后 将两个着色器加进去   然后 将两个着色器连接起来

  var shaderProgram = gl.createProgram()

    //分别附加两个已编译好的着色器对象

    gl.attachShader(shaderProgram, vertShader)

    gl.attachShader(shaderProgram, fragShader)

    //链接两个附件加好的着色器程序

    gl.linkProgram(shaderProgram)

  gl.useProgram(shaderProgram)

7 然后就可以绘画了 无语的是绘画这里还有个坑 第一个参数代表了绘画的类型  图形 线和点都是不一样的

  gl.drawArrays(gl.TRIANGLES, 0, 3)

8不使用变量的话 这里就结束了

使用变量的话  

需要一个中间第三方 缓存区 将变量的值赋值给第三方 然后 再从第三方去取 

可以理解为不可以直接将我们的js 的变量传给webgl使用

 流程就是 

  创建一个缓存区 

  然后绑定缓存区  其实就是定义他的类型

得到webgl执行程序的变量 这个是我们js理解的那种变量 不是具体的值 只是声明得一个变量

然后将变量值赋值  ( 使用这个API 会直接将缓存区的值赋值到变量中 第一个参数是数字是说第几个变量 webgl自己算出来的 )

开启变量的使用就可以了

var vertexBuffer = gl.createBuffer()

    //说明缓存对象保存的类型

    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)

    //写入坐标数据

    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)

    //获取到数组中单个元素的字节数

    var FSIZE = vertices.BYTES_PER_ELEMENT

    //获取到顶点着色器中变量

    var a_Position = gl.getAttribLocation(shaderProgram, 'a_Position')

    //将坐标值赋值给变量

    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0)

    //开启变量值的使用

    gl.enableVertexAttribArray(a_Position)

代码

<body>
  <canvas id="cvs" width="200" height="200" style="border: dashed 1px red">
    你的浏览器不支持画布元素
  </canvas>
  <script type="text/javascript">
    //获取画布元素
    var cvs = document.getElementById('cvs')
    //获取到元素的上下文环境对象
    var gl = cvs.getContext('webgl')

    //顶点着色器变量
    var VSHADER_SOURCE =
      //使用存储限定符定义一个接受顶点坐标的变量
      'attribute vec4 a_Position;' +
      'attribute vec4 a_Color;' +
      'varying vec4 v_Color;' +
      'void main() {' +
      //定义点的坐标并转换成变量保存
      'gl_Position = a_Position; ' +
      'v_Color = a_Color; ' +
      '} '

    //片段着色器变量
    var FSHADER_SOURCE =
      'precision mediump float;' +
      'varying vec4 v_Color;' +
      'void main() {' +
      //设置图形像素的颜色并保存
      'gl_FragColor = v_Color ;' +
      '}'

    //新建一个用于装顶点字符串的着色器对象
    var vertShader = gl.createShader(gl.VERTEX_SHADER)
    //加载保存好的顶点代码字符串变量
    gl.shaderSource(vertShader, VSHADER_SOURCE)
    //编译顶点着色器
    gl.compileShader(vertShader)

    //新建一个用于装片段字符串的着色器对象
    var fragShader = gl.createShader(gl.FRAGMENT_SHADER)
    //加载保存好的片段代码字符串变量
    gl.shaderSource(fragShader, FSHADER_SOURCE)
    //编译片段着色器
    gl.compileShader(fragShader)

    //新建一个程序
    var shaderProgram = gl.createProgram()

    //分别附加两个已编译好的着色器对象
    gl.attachShader(shaderProgram, vertShader)
    gl.attachShader(shaderProgram, fragShader)

    //链接两个附件加好的着色器程序
    gl.linkProgram(shaderProgram)
    //开启程序的使用
    gl.useProgram(shaderProgram)
    //定义一个类型数组保存顶点坐标值
    var vertices = new Float32Array([
      //  x,   y,   red, green, blue
      0.0, 0.5, 1.0, 0.0, 0.0, -0.5, -0.5, 0.0, 1.0, 0.0, 0.5, -0.5, 0.0, 0.0,
      1.0,
    ])
    //先创建一个缓存对象
    var vertexBuffer = gl.createBuffer()
    //说明缓存对象保存的类型
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer)
    //写入坐标数据
    gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW)
    //获取到数组中单个元素的字节数
    var FSIZE = vertices.BYTES_PER_ELEMENT

    //获取到顶点着色器中变量
    var a_Position = gl.getAttribLocation(shaderProgram, 'a_Position')
    //将坐标值赋值给变量
    gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 5, 0)
    //开启变量值的使用
    gl.enableVertexAttribArray(a_Position)

    //获取到顶点着色器中变量
    var a_Color = gl.getAttribLocation(shaderProgram, 'a_Color')
    //将坐标值赋值给变量
    gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 5, FSIZE * 2)
    //开启变量值的使用
    gl.enableVertexAttribArray(a_Color)

    //绘制指定位置的图形
    gl.drawArrays(gl.TRIANGLES, 0, 3)
  </script>
</body>

这是webgl 的简单使用 

three.js的 着色器 底层是他  但是使用却不完全一样的

首先声明好three的要素对象  场景 相机 渲染器

最简单的 是可以只传坐标 着色器都不写的 因为three已经写好了

可是我想传颜色 就需要 我们自己写一下了

说一下流程 

首先定义颜色和坐标

然后给几何体设置这两个属性 (然后在main函数里面这两个就是默认的变量了)

然后定义着色器 因为颜色是变量需要传进去  所以没办法  只能定义着色器了

这里用到了gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0)

   gl_FragColor = vec4(vColor, 1.0); 

1 注意 两个共用的变量使用    varying  来声明

2 projectionMatrix * modelViewMatrix * vec4(position, 1.0) 这个算出来的就是世界坐标

  在这个场景中有各种各样的坐标系  这是几个坐标系的转化  可以这么简单的记 这就是转化公式 到了世界坐标系

3 vertexColors: true, 要想颜色生效需要这个属性

4 必须加; 

5 三点确定一个平面如果需要更多的点做一个矩形而不是三角形 则需要geometry.setIndex()

需要特别注意的事四个点的方向必须是这四个0123 否则渲染不出来

three.js(13)-三角形面 - 简书

img.png

我们把图形的顶点做个索引编号,其中0,1,2顶点可以确定一个三角形面,而2,3,0可以再确定一个三角形面,二者合起来就是一个完整的矩形了。

   var index = [
        0,1,2,
        2,3,0
    ];

最后的代码

<template>
  <div id="my-three"></div>
</template>
<script lang="ts" setup>
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import { onMounted } from "vue";

//创建一个三维场景
const scene = new THREE.Scene();
//创建一个物体(形状)
const geometry = new THREE.BufferGeometry();
const points = [0, 0, 5, 5, 0, 5, -5, 5, 0];
// const points=[]
// points.push(2.5,2.5,0)
// points.push(-2.5,2.5,0)
// points.push(0,0,2.5)
geometry.setAttribute("position", new THREE.Float32BufferAttribute(points, 3));
const colors = [1, 0, 0, 6.6613381, 1, 0, 0, 6.661, 1];

geometry.setAttribute("color", new THREE.Float32BufferAttribute(colors, 3));
//创建材质(外观)
const material = new THREE.ShaderMaterial({
  vertexShader: `
        varying vec3 vColor;
        void main() {
          vColor = color;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
        }`,
  fragmentShader: `
        varying vec3 vColor;
        void main() {
          gl_FragColor = vec4(vColor, 1.0);
        }`,
  side: THREE.FrontSide,
  blending: THREE.AdditiveBlending,
  depthTest: false,
  transparent: true,
  vertexColors: true,
});
//创建一个网格模型对象
const mesh = new THREE.Mesh(geometry, material); //网络模型对象Mesh
//把网格模型添加到三维场景
scene.add(mesh); //网络模型添加到场景中
mesh.position.set(0, 0, 0);

//添加光源 //会照亮场景里的全部物体(氛围灯),前提是物体是可以接受灯光的,这种灯是无方向的,即不会有阴影。
const ambient = new THREE.AmbientLight(0xffffff, 0.4);
const light = new THREE.PointLight("yellow", 1); //点光源,color:灯光颜色,intensity:光照强度

scene.add(ambient);
light.position.set(200, 300, 400);

scene.add(light);

//创建一个透视相机,窗口宽度,窗口高度
const width = window.innerWidth,
  height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(45, width / height, 1, 1000);

//设置相机位置  相机的位置
camera.position.set(300, 300, 300);
//设置相机方向  相机的摄像头对准的位置
camera.lookAt(0, 0, 0);

//创建辅助坐标轴
const axesHelper = new THREE.AxesHelper(200); //参数200标示坐标系大小,可以根据场景大小去设置
scene.add(axesHelper);

//创建一个WebGL渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height); //设置渲染区尺寸
renderer.render(scene, camera); //执行渲染操作、指定场景、相机作为参数

const controls = new OrbitControls(camera, renderer.domElement); //创建控件对象
controls.addEventListener("change", () => {
  renderer.render(scene, camera); //监听鼠标,键盘事件
});
onMounted(() => {
  document.getElementById("my-three")?.appendChild(renderer.domElement);
});
</script>
<style scoped></style>

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值