ThreeJS-3D教学十二:ShaderMaterial

news2025/1/16 13:41:02

一、首先 Shader 是做什么的
Shader 可以自定义每个顶点、每个片元/像素如何显示,而控制顶点和片元显示是通过设置 vertexShader 顶点着色器和 fragmentShader 片元着色器,这两个着色器用在 ShaderMaterial 和 RawShaderMaterial 材质上。
我们先看一个例子:
在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head>
	<title>three.js webgl - raw shader</title>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
</head>
<body>
<div id="container"></div>
<script id="vertexShader" type="x-shader/x-vertex">
	uniform mat4 modelViewMatrix;
	uniform mat4 projectionMatrix;
	attribute vec3 position;
	void main()	{
		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
	}
</script>

<script id="fragmentShader" type="x-shader/x-fragment">
	void main()	{
		gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
	}

</script>
<script type="importmap">
	{
		"imports": {
          "three": "../three-155/build/three.module.js",
          "three/addons/": "../three-155/examples/jsm/"
        }
	}
</script>

<script type="module">
  import * as THREE from 'three';
  import Stats from 'three/addons/libs/stats.module.js';
  import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
  let container, stats, controls;
  let camera, scene, renderer;

  init();
  animate();
  initObject();

  function initObject() {
    // geometry
    // 第一个是生成几个三角形 
    const vertexCount = 2 * 3;
    const geometry = new THREE.BufferGeometry();
    const positions = [];
    const colors = [];

    for ( let i = 0; i < vertexCount; i ++ ) {
      // adding x,y,z
      positions.push( Math.random() - 0.5 );
      positions.push( Math.random() - 0.5 );
      positions.push( Math.random() - 0.5 );
      // adding r,g,b,a
      colors.push( Math.random() * 255 );
      colors.push( Math.random() * 255 );
      colors.push( Math.random() * 255 );
      colors.push( Math.random() * 255 );
    }

    const positionAttribute = new THREE.Float32BufferAttribute( positions, 3 );
    const colorAttribute = new THREE.Uint8BufferAttribute( colors, 4 );
    colorAttribute.normalized = true; // this will map the buffer values to 0.0f - +1.0f in the shader
    geometry.setAttribute( 'position', positionAttribute );
    geometry.setAttribute( 'color', colorAttribute );

    // material
    const material = new THREE.RawShaderMaterial({
      uniforms: {
        time: { value: 1.0 }
      },
      vertexShader: document.getElementById( 'vertexShader' ).textContent,
      fragmentShader: document.getElementById( 'fragmentShader' ).textContent,
      side: THREE.DoubleSide,
      transparent: true
    });
    const mesh = new THREE.Mesh( geometry, material );
    scene.add( mesh );
  }

  function init() {
    container = document.getElementById( 'container' );
    camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 10 );
    camera.position.z = 2;
    scene = new THREE.Scene();
    scene.background = new THREE.Color( 0x101010 );
    renderer = new THREE.WebGLRenderer();
    renderer.setPixelRatio( window.devicePixelRatio );
    renderer.setSize( window.innerWidth, window.innerHeight );
    container.appendChild( renderer.domElement );
    controls = new OrbitControls( camera, renderer.domElement );
    stats = new Stats();
    container.appendChild( stats.dom );
    window.addEventListener( 'resize', onWindowResize );

  }

  function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
  }

  function animate() {
    requestAnimationFrame( animate );
    render();
    controls.update();
    stats.update();
  }
  function render() {
    const time = performance.now();
    const object = scene.children[0];
    if (object) {
      // object.rotation.y = time * 0.0005;
      object.material.uniforms.time.value = time * 0.005;
    }
    renderer.render( scene, camera );
  }
</script>
</body>
</html>

以上代码 顶点着色器 我们用的是固定写法计算顶点的位置

gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
或者也可以这样:
gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4( position, 1.0 );

而片元着色器我们设置了一个白色
我相信大家会对 scropt 中 x-shader/x-vertex、x-shader/x-fragment 很陌生,没关系咱们学习 Shader 其实大部分就是学这里面怎么写:
它是一种类似C语言的 GLSL 语言——即 OpenGL Shading Language——,
JavaScript、C 等语言通常在 CPU 上执行,而着色器语言通常在 GPU 上执行,由 GPU 分别对每个顶点、每个片元独立执行

shader 程序可以单独写在诸如 vertex.glsl、fragment.glsl 的文件里再导入使用,也可以和示例一样写在script中,或者在 JavaScript 里用字符串格式表示(后面会介绍)

在顶点着色器里需要设置 gl_Position顶点位置,在片元着色器里需要设置 gl_FragColor 片元/像素颜色,两者都在没有返回值的 void main() {} 主函数里设置,并且 main 函数会被自动执行

着色器语言三种变量 attribute、uniform 和 varying

  • 简单总结
    顶点着色器渲染定位顶点位置
    片段着色器为该几何体的每个可见片元(像素)进行着色
    片段着色器在顶点着色器之后执行
    在每个顶点之间会有变化的数据(如顶点的位置)称为attribute,只能在顶点着色器中使用
    顶点之间不变的数据(如网格位置或颜色)称为uniform,可以在顶点着色器和片段着色器中使用
    从顶点着色器发送到片元着色器中的插值计算数据被称为varying

看了上面的内容,我们再来看一个案例,提前说下 在这里我无意将所有 GLSL 语言的方法一一列出,我相信大家也不愿意看,毕竟网上一大堆类似文章,官网上也能看,我只是通过案例,把一些常用的知识点给到大家,能让各位对 编写Shader有个初步的认知:

let vertexShader = `
    precision mediump float;
	precision mediump int;
	uniform mat4 modelViewMatrix;
	uniform mat4 projectionMatrix;
	attribute vec3 position;
	attribute vec4 color;
	varying vec3 vPosition;
	varying vec4 vColor;
	void main()	{
		vPosition = position;
		vColor = color;
		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
	}
`;
let fragmentShader = `
    precision mediump float;
	precision mediump int;
	uniform float time;
	varying vec3 vPosition;
	varying vec4 vColor;
	void main()	{
		vec4 color = vec4( vColor );
		color.g += sin( vPosition.x * 10.0 + time ) * 0.5;
		gl_FragColor = color;
	}
`;

上面有几点知识点,先从简单的来,
1、通过 varying 可以将vPosition、vColor 从 vertexShader 到 fragmentShader
2、上面的写法是 shader 程序刚才提到的 字符串格式写法
3、precision 一个新的知识点 - 着色器运算精度设置
通过设置着色器数值的精度可以更好的配置资源,可以根据需要,在不太影响渲染效果前提下,可以尽量降低运算精度。
lowp、mediump和highp关键字 分别对应 低、中、高三个精度

1)通过precision关键字可以批量声明一些变量精度。
比如顶点着色器代码设置precision highp float;,表示顶点着色器中所有浮点数精度为高精度。

2)比如片元着色器代码设置precision lowp int;,表示片元着色器中所有整型数精度为低精度。

3)顶点和片元着色器不同类型数据默认精度
顶点着色器默认精度

数据类型默认精度
int高精度hight
float高度hight
sampler2D低精度lowp
samplerCube低精度lowp

片元着色器默认精度

数据类型默认精度
int中精度mediump
float无默认值,如果片元着色器用到浮点数,注意一定手动设置
sampler2D低精度lowp
samplerCube低精度lowp

4、我们发现有申明 modelViewMatrix、projectionMatrix、position、color变量,这是因为我们使用 RawShaderMaterial材质,这个方法没有默认的内置变量声明,与之对应的我们可以用 ShaderMaterial 材质,此时就可以这样写:

let vertexShader = `
    precision mediump float;
	precision mediump int;
	varying vec3 vPosition;
	varying vec4 vColor;
	void main()	{
		vPosition = position;
		vColor = color;
		gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
	}
`;

更多的 内置变量请看这里
通过以上案例我们算是简单的了解了 Shader,发现 Shader其实就是改变 顶点位置 和 片元/像素颜色

二、图形构成的基础 三角形
我们将 geometry 换为一个球体来认识一下图形的基础构成

const geometry = new THREE.SphereGeometry( 0.5, 16, 8 );
const material = new THREE.RawShaderMaterial({

          uniforms: {
            time: { value: 1.0 }
          },
          vertexShader: document.getElementById( 'vertexShader1' ).textContent,
          fragmentShader: document.getElementById( 'fragmentShader1' ).textContent,
          side: THREE.DoubleSide,
          transparent: false,
          wireframe: true  // 将几何体渲染为线框,默认值为false(即渲染为平面多边形)。
        });

在这里插入图片描述
通过这个球体 可以很好的理解 图形的构成,其实就是一个个的三角形,三角形越是多,图形效果越好 当然对电脑性能要求越高,

const geometry = new THREE.SphereGeometry( 0.5, 128, 64);

在这里插入图片描述
可以看到 这个球体就很完美了,通过这个案例也能更好的帮大家理解
顶点着色器渲染顶点位置的顶点 是哪些点、从哪来的点
片段着色器为该几何体的每个可见片元(像素)进行着色

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1881690.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringMVC系列八: 手动实现SpringMVC底层机制-第二阶段

实现任务阶段三 &#x1f34d;从web.xml动态获取zzwspringmvc.xml 说明: 前面我们加载zzwspringmvc.xml是硬编码, 现在做活. 从web.xml动态获取 示意图[分析说明] 1.ZzwDispatcherServlet在创建并初始化ZzwWebApplicationContext时, 动态地从web.xml中获取到spring配置文…

Linux中定位JVM问题常用命令

查询Java进程ID #ps axu | grep java #ps elf | grep java查看机器负载及CPU信息 #top -p 1(进程ID) #top (查看所有进程)获取CPU飙升线程堆栈 1. top -c 找到CPU飙升进程ID&#xff1b; 2. top -Hbp 9702(替换成进程ID) 找到CPU飙升线程ID&#xff1b; 3. $ printf &quo…

Redis 必备的原理知识要点

1、为什么使用缓存&#xff1f; 1、数据库太慢-无法使用 2、高性能 3、高并发 4、热点高频数据 2、redis合适的场景&#xff1f; 1、数据类型 5种&#xff1b; 2、Bitmap&#xff1b; 3、HyperLogLog&#xff1b; 结合自身项目讲解redis的使用&#xff1a; 1、token&a…

考研数学|考到110到底有多难?

考研数学一要考到110分&#xff0c;确实不容易。数学一的难度在于它涉及的知识点多&#xff0c;包括高等数学、线性代数和概率论。这些内容不仅要求你理解深刻&#xff0c;还要能熟练运用。特别是高等数学&#xff0c;它在考试中的分值占比最高&#xff0c;也是最难的部分。 数…

SAMformer:通过锐度感知最小化和通道注意力解锁变换器在时间序列预测中的潜力

目录 摘要1. 引言当前方法的局限性变换器的可训练性我们贡献的总结 2. 提出的方法符号说明2.1 问题设置2.2 激励示例命题2.1&#xff08;最优解的存在性&#xff09; 2.3 变换器的损失景观现有的解决方案 2.4. SAMformer&#xff1a;集成所有方法 3. 实验3.1 主要收获 摘要 基…

目标检测算法的研究现状

一、引言 目标检测是计算机视觉领域中的一项重要任务&#xff0c;旨在从图像或视频中识别并定位出感兴趣的目标物体。随着深度学习技术的快速发展&#xff0c;目标检测算法取得了显著的进步&#xff0c;并在自动驾驶、智能安防、人脸识别等领域得到了广泛应用。本文将对目标检…

2024上海初中生古诗文大会倒计时4个月:单选题真题和独家解析

现在距离2024年初中生古诗文大会初选还有4个多月时间&#xff08;11月3日正式开赛&#xff09;&#xff0c;我们继续来看10道选择题真题和详细解析&#xff0c;以下题目截取自我独家制作的在线真题集&#xff0c;都是来自于历届真题&#xff0c;去重、合并后&#xff0c;每道题…

电商价格监测:品牌控价维权的关键利器

品牌在进行控价时&#xff0c;所面对的是线上成千上万条的商品链接&#xff0c;如果仅依靠人工&#xff0c;根本无法做到准确且全面地完成电商价格监测工作。因此&#xff0c;一套准确率高的电商价格监测系统对于品牌的控价维权而言&#xff0c;其重要性不言而喻。 在形形色色的…

UE4_材质_材质节点_视差偏移BumpOffset

一、定义 凹凸贴图偏移&#xff08;BumpOffset&#xff09; 是虚幻引擎4术语&#xff0c;就是通常所谓的"视差贴图"。BumpOffset表达式可以使材质产生深度错觉&#xff0c;而不需要额外的几何体。BumpOffset材质使用灰阶_高度贴图_来提供深度信息。高度贴图中的值越…

沃创云获客系统如何帮助企业找到意向客户群体

沃创云是一家做外呼系统起家&#xff0c;越来越多客户有打电话的需求。我们突然意识到&#xff0c;大量的数据积累是电销的基础&#xff0c;那么如何找到客户以及联系方式也非常关键 通过爬虫技术&#xff0c;通过帮助企业精准地找到意向客户群体。以下是该系统如何帮助企业实…

PHP转Go系列 | GET 和 POST 请求的使用姿势

大家好&#xff0c;我是码农先森。 说到 HTTP 请求工具想必对我们做 Web 开发的程序员都不陌生&#xff0c;只要涉及到网络请求都必须使用。对于我们 PHP 程序员来说&#xff0c;最熟悉不过的就是 CURL 扩展&#xff0c;只要安装的这个扩展便可随意发起 HTTP 请求。 但在 PHP …

Transformer常见面试题

目录 1.Transformer为何使用多头注意力机制&#xff1f;&#xff08;为什么不使用一个头&#xff09; 2.Transformer为什么Q和K使用不同的权重矩阵生成&#xff0c;为何不能使用同一个值进行自身的点乘&#xff1f; &#xff08;注意和第一个问题的区别&#xff09; 3.Transf…

React+TS前台项目实战(二十二)-- 全局常用导出组件Export封装

文章目录 前言Export组件1. 功能分析2. 代码详细注释3. 使用方式4. 效果展示 总结 前言 今天我们来封装一个带导出图标的导出组件。 Export组件 1. 功能分析 通过传入链接地址&#xff0c;规定要跳转的导出页面&#xff0c;或是直接通过链接导出数据 2. 代码详细注释 // /c…

产品中心|高效能双处理器Xilinx FPGA 4通道射频收发板卡

1、产品概述 基于Xilinx XC7K325T芯片的4通道射频收发板卡&#xff0c;搭载高能效Cortex-A8内核处理器、1组16bit/2GB DDR3及1组4GB DDR3、 1组2GB Nand Flash、1路USB接口、4路高速ADC、4路高速DAC&#xff0c;支持外触发&#xff0c;外时钟。用于FPGA程序加载板卡工作温度范…

前端开发中实用小技巧

运行javascript小技巧 // 1.直接在浏览器的地址栏中输入一下代码&#xff1a;javascript:alert(hello world) // 2.注意事项ie和chrom回自动去掉开头的 【javascript:】需要手动添加火狐浏览器不支持这个技巧 // 3.场景快速测试一段js代码运行HTML代码的神奇技巧 // 1.直接在…

TensorFlow安装CPU版本和GPU版本

文章目录 前言一、TensorFlow安装CPU版本1.新建虚拟环境2.激活虚拟环境3.下载tensorflow4.验证是否下载成功 二、TensorFlow安装GPU版本1.新建虚拟环境2.激活虚拟环境3.安装tensorflow-gpu4.验证是否下载成功 前言 下载的Anaconda是Anaconda3-2024.02-1-Windows-x86_64版本 一…

firewalld防火墙转发流量到其他端口forward port rules

假设云主机eth0: 47.93.27.106 tun0: inet 10.8.0.1 netmask 255.255.255.0 Show rules for a specific zone (public) sudo firewall-cmd --zonepublic --list-all Add the tun0 interface to the public zone: sudo firewall-cmd --zonepublic --add-interfacetun0 --…

MTK7621交换芯片配置

MTK7621上自带的交换芯片为mt7530 admin@OpenWrt:~# /sbin/swconfig list Found: switch0 - mt7530 交换芯片的配置工具为swconfig程序。MTK7621采用内部的MDIO(Management Data Input/Output)接口管理MT7530的switch芯片。 MT7530共有7个物理口,通过/sbin/swconfig dev …

isupper()方法——判断字符串是否全由大写字母组成

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 语法参考 isupper()方法用于判断字符串中所有的字母是否都是大写。isupper()方法的语法格式如下&#xff1a; str.isupper() 如果字符串中包含至少…

linux虚拟机部署的MySQL如何使用外网访问?教你轻松使用cpolar在centos搭建内网穿透

文章目录 写在前面实现Linux的内网穿透1、官网账号注册2、在Linux部署我们自己的项目3、一键自动下载安装cpolar4、设置自己的token5、启动cpolar服务6、MySQL穿透测试 卸载方法 写在前面 相信很多小伙伴在本地搭建了一个MySQL数据库&#xff0c;想让其他同事或者合作者一起使…