Orillusion

    • 注册
    • 登录
    • 搜索
    • 版块
    • 最新
    • 标签

    orillusion入门系列四 | 材质

    中文社区
    orillusion引擎 engine
    1
    1
    169
    正在加载更多帖子
    • 从旧到新
    • 从新到旧
    • 最多赞同
    回复
    • 在新帖中回复
    登录后回复
    此主题已被删除。只有拥有主题管理权限的用户可以查看。
    • O
      oldguy 最后由 oldguy 编辑

      如果说人类最难以满足的两个器官是舌头和眼睛,舌头费调料眼睛费显卡,你同意吗?今天继续学习如何愉悦我们视觉的技术,那就是材质(Material)。在上一期的示例已经可以创造几种规则的几何体了,但是看起来光秃秃的,这些几何体很难称之为物体,今天我们从认识材质开始,到使用材质使物体的外观丰富起来。

      什么是材质(Material)

      材质是由shader定义的一组特性集合,用来定义对光的处理。光对于材质来说起着决定的作用,当没有光时我们看不到任何物体,我们视觉看到的物体是由光来决定的,光照射到物体的表面会产生反射、折射,物体也会吸收一部分,当然这些反射又会照射到其它物体上又会产生反复的吸收以及折射,那么如何在计算性能和逼真度上做取舍呢,这里不做原理上的探究,只聊一聊感性上的体会。一个立方体看起来是一块砖头还是一块金锭,是由不同的材质定义的,材质可以使物体看起来更像什么,以及是否足够逼真。

      材质、贴图、纹理的关系

      如果只有材质还是比较好理解的,忽然面向三个相近的概念需要在理解上做个区分。
      笼统的来说材质包含贴图,贴图包含纹理。材质是一个集合,内部包含了光学特性,贴图资源等等。贴图是物理的表面,可以对图片视频等效果的处理,例如拉伸,缩放,与物体的贴合参数等。纹理可以简单理解成图片视频等原材料。

      材质类型

      材质的类型是引擎给我们提供的又一有力工具,类同于前面引擎封装的几款几何体,引擎同样为实际应用中的几种典型材质做了封装。在使用引擎开发时选择与实际需要最贴近类型的材质组件就可以,材质类型是可以自定义的,我们先从熟悉引擎提供的材质开始。
      还记得前文介绍的网格(Mesh)吗,每个网格至少由几何数据和材质数据组成,不同的类型对应不同的材质类,需要对应的材质类型直接实例化该类型的材质,赋值于网络的材质属性。

      UnLitMaterial(无光材质)

      这种材质可以简单称为无光材质,忽略所有的光照的计算,所以计算性能非常高,可以作为背景使用,由于没有光的影响也可以作为一些不受光影响的特殊效果。

      支持的参数

      这里试用几个参数,更多的参数还要看官方文档。

      • baseColor(基础颜色):对材质的基础颜色进行设置,设置后覆盖了该材质的物体看起来会叠加上基础颜色。
      let color:Color = new Color();
      let material:UnLitMaterial = new UnLitMaterial();
      material.baseColor = color;
      
      • baseMap(基础贴图):对材质的基础贴图进行设置,设置后覆盖了该材质的物体会由该贴图覆盖

      完成代码

      前几次我们对一个立方体非常熟悉了,今天我们仍然创建一个立方体,但是使用UnLitMaterial材质,并覆盖一张贴图,同时可以动态的调整基础颜色。

      import { Engine3D, Scene3D, Object3D, Camera3D, HoverCameraController, MeshRenderer, BoxGeometry, HDRLitMaterial, ForwardRenderJob, Stats, UnLitMaterial, Color } from "@orillusion/core";
      import * as dat from 'dat.gui';
      
      export default class Unlit {
      
          cameraObj: Object3D;
      
          camera: Camera3D;
      
          scene: Scene3D;
      
          boxObj: Object3D;
      
          material:UnLitMaterial;
      
          async run() {
      
              await this.init();
      
              await this.setup();
      
              await this.start();
          }
      
          /***
           * 配置并初始化引擎
           */
          private async init() {
              // 初始化引擎
              await Engine3D.init();
          }
      
          /**
           * 引擎功能代码
           */
          private async setup() {
              // 创建一个场景
              this.scene = new Scene3D();
      
              // 添加性能监控面板
              this.scene.addComponent(Stats);
              // 创建一个相机
              this.cameraObj = new Object3D();
              this.camera = this.cameraObj.addComponent(Camera3D);
              // 设置相机类型
              this.camera.perspective(60, window.innerWidth / window.innerHeight, 1, 5000.0);
              // 设置相机控制器
              let controller = this.cameraObj.addComponent(HoverCameraController);
              controller.setCamera(20, -20, 25);
              // 添加相机至场景
              this.scene.addChild(this.cameraObj);
      
              this.createBox();
      
              this.addGUI();
      
          }
      
          /**
           * 启动渲染
           */
          private async start() {
              // 创建前向渲染
              let renderJob: ForwardRenderJob = new ForwardRenderJob(this.scene);
              // 开始渲染
              Engine3D.startRender(renderJob);
          }
      
          /**
           * 创建立方体
           */
          private async createBox() {
              // 创建一个对象
              this.boxObj = new Object3D();
              // 挂载脚本
              // 创建渲染组件
              let mr: MeshRenderer = this.boxObj.addComponent(MeshRenderer);
              // 设置形状
              mr.geometry = new BoxGeometry(5, 5, 5);
      
              this.material = new UnLitMaterial();
      
              // 设置基础贴图
              this.material.baseMap = await Engine3D.res.loadTexture('textures/diffuse.jpg');
              // 设置材质
              mr.material = this.material;
              // 添加到场景
              this.scene.addChild(this.boxObj);
      
          }
      
          private async addGUI(){
              // 创建 dat 实例
              const gui = new dat.GUI();
              // 创建保存属性值对象
              const materialInfo = {
                  baseColor:'#fff',
              };
      
              // 设置材质颜色
              gui.addColor(materialInfo, "baseColor").onChange((v) => {
                  console.log('baseColor:', v);
                  let color:Color = new Color();
                  color.setHex(v);
                  this.material.baseColor = color;
              });
          }
      
      }
      

      运行效果如下:
      e3720fc2-d2f9-478a-bf76-9c1f4139a3ff-image.png
      调整颜色会看到颜色会叠加到物体上。

      LambertMaterial(兰伯特材质)

      材质是与光分不开的,Lambert材质是与Lambert光照模型对应的,简单说一句Lambert是一种经验性质的简化的光照模型,据说只处理漫反射,其它类型全忽略,因此Lambert只能模拟粗糙的物体表面,像镜面类的完全不适合,如果是衣料,石头之类的表面粗糙的非常适用。

      支持的参数

      与Unlit类同。

      完成代码

      代码与Unlit也是类同的,只是把材质的类型由UnlitMaterial换成LambertMaterial,这里就不占用篇幅了。
      看一下基础的运行效果:
      31a588c4-04ef-46f5-a597-48c7733a52b1-image.png

      HDRLitMaterial(物理材质)

      能看到Lmabert比Unlit更进了一步,尽管只考虑了一种光,至少有光的参与了,可见材质也是不断的丰富起来,计算的因素多了起来,与实际效果更接近了。看到各大引擎都是主推物理材质,所以除非计算机性能实在拉垮,否则还是多熟悉使用物理材质吧,大势所趋。过去我们的例子默认也是用的这种材质。

      支持参数

      物理材质支持的参数比较多,这里我们只验证个别几个属性,更多的属性可以对着文档一一验证。

      • baseColor(基础颜色):材质本身的颜色,可以使用一个Color类型的变量设置
      • roughness(粗糙度):模拟物体表面粗糙的程度,随着数值变大物体会看起来更粗糙
      • metallic(金属度):模拟物体表面金属的程度,随着数值的变化物体会看起来更光滑

      运行效果

      使用这三个参数调节显示效果如下:
      7f4b687d-e333-4f87-9e7e-f9a0b9bf0608-image.png

      综合示例

      不同的材质主要还是对光的处理不同,这里我们写一个综合的实例,将三种材质放到一个场景下,在一个聚光灯照射下观察效果,这里我们提前借用一下光源的组件,可以暂时不必在意,后续会专门介绍。

      示例代码

      import { Engine3D, Scene3D, Object3D, Camera3D, HoverCameraController, MeshRenderer, BoxGeometry, HDRLitMaterial, ForwardRenderJob, Stats, Color, LambertMaterial, UnLitMaterial, Vector3, MaterialBase, SpotLight, SphereGeometry, defaultTexture } from "@orillusion/core";
      import * as dat from 'dat.gui';
      
      export default class Materials {
      
          spotLight: SpotLight;
      
          cameraObj: Object3D;
      
          camera: Camera3D;
      
          scene: Scene3D;
      
          boxObj: Object3D;
      
          hdrMaterial: HDRLitMaterial;
          lambertMaterial: LambertMaterial;
          unlitMaterial: UnLitMaterial;
      
          async run() {
      
              await this.init();
              await this.setup();
      
              await this.start();
          }
      
          /***
           * 配置并初始化引擎
           */
          private async init() {
              // 初始化引擎
              await Engine3D.init();
          }
      
          /**
           * 引擎功能代码
           */
          private async setup() {
              // 创建一个场景
              this.scene = new Scene3D();
      
              // 添加性能监控面板
              this.scene.addComponent(Stats);
              // 创建一个相机
              this.cameraObj = new Object3D();
              this.camera = this.cameraObj.addComponent(Camera3D);
              // 设置相机类型
              this.camera.perspective(60, window.innerWidth / window.innerHeight, 1, 5000.0);
              // 设置相机控制器
              let controller = this.cameraObj.addComponent(HoverCameraController);
              controller.setCamera(20, -20, 25);
              // 添加相机至场景
              this.scene.addChild(this.cameraObj);
      
              this.hdrMaterial = new HDRLitMaterial();
              this.lambertMaterial = new LambertMaterial();
              this.unlitMaterial = new UnLitMaterial();
      
              this.createBox(new Vector3(-10, 0, 0), this.unlitMaterial);
              this.createBox(new Vector3(0, 0, 0), this.lambertMaterial);
              this.createBox(new Vector3(10, 0, 0), this.hdrMaterial);
      
              this.createLight();
              this.createFloor();
      
              this.addGUI();
      
          }
      
          /**
           * 启动渲染
           */
          private async start() {
              // 创建前向渲染
              let renderJob: ForwardRenderJob = new ForwardRenderJob(this.scene);
              // 开始渲染
              Engine3D.startRender(renderJob);
          }
      
          private async createLight() {
              let sp = new SphereGeometry(0.5, 10, 10);
      
              let spotLightObj = new Object3D();
              let mr = spotLightObj.addComponent(MeshRenderer);
              mr.geometry = sp;
              mr.material = new HDRLitMaterial();
      
              this.spotLight = spotLightObj.addComponent(SpotLight);
              this.scene.addChild(spotLightObj);
              spotLightObj.y = 10;
      
          }
      
          /**
           * 创建立方体
           */
          private async createBox(location: Vector3, matrix: MaterialBase) {
              // 创建一个对象
              this.boxObj = new Object3D();
              this.boxObj.localPosition = location;
              // 创建渲染组件
              let mr: MeshRenderer = this.boxObj.addComponent(MeshRenderer);
              // 设置形状
              mr.geometry = new BoxGeometry(5, 5, 5);
      
              // 设置材质
              mr.material = matrix;
              // 添加到场景
              this.scene.addChild(this.boxObj);
      
          }
      
          private async createFloor(){
              let floor = new Object3D();
              floor.y = -2.5;
              let mat = new HDRLitMaterial();
              mat.baseMap = defaultTexture.grayTexture;
              let mr = floor.addComponent(MeshRenderer);
              mr.geometry = new BoxGeometry(40, 1, 30);
              mr.material = mat;
              this.scene.addChild(floor);
          }
      
          private async addGUI() {
              // 创建 dat 实例
              const gui = new dat.GUI();
      
              const lightInfo = {
                  x:0,
                  y:0,
                  z:0,
                  rotationX:0,
                  rotationY:0,
                  rotationZ:0,
                  lightColor:'#fff',
              }
      
              let lightFolder = gui.addFolder("Light");
              lightFolder.add(lightInfo, "x", -10, 10).onChange((v) => {
                  console.log('x:', v);
                  this.spotLight.transform.x = v;
              });
              lightFolder.add(lightInfo, "y", -10, 10).onChange((v) => {
                  console.log('y:', v);
                  this.spotLight.transform.y = v;
              });
              lightFolder.add(lightInfo, "z", -10, 10).onChange((v) => {
                  console.log('z:', v);
                  this.spotLight.transform.z = v;
              });
              lightFolder.add(lightInfo, "rotationX", -360, 360).onChange((v) => {
                  console.log('rotationX:', v);
                  this.spotLight.transform.rotationX = v;
              });
              lightFolder.add(lightInfo, "rotationY", -360, 360).onChange((v) => {
                  console.log('rotationY:', v);
                  this.spotLight.transform.rotationY = v;
              });
              lightFolder.add(lightInfo, "rotationZ", -360, 360).onChange((v) => {
                  console.log('rotationZ:', v);
                  this.spotLight.transform.rotationZ = v;
              });
              lightFolder.addColor(lightInfo, "lightColor").onChange((v) => {
                  console.log('lightColor:', v);
                  let color: Color = new Color();
                  color.setHex(v);
                  this.spotLight.lightColor = color;
              });
      
              // 创建保存属性值对象
              const unlitInfo = {
                  baseColor: '#fff'
      
              };
      
              let unlitFolder = gui.addFolder("UnlitMaterial");
              // 设置材质颜色
              unlitFolder.addColor(unlitInfo, "baseColor").onChange((v) => {
                  console.log('baseColor:', v);
                  let color: Color = new Color();
                  color.setHex(v);
                  this.unlitMaterial.baseColor = color;
              });
      
              // 创建保存属性值对象
              const lambertInfo = {
                  baseColor: '#fff'
      
              };
      
              let lambertFolder = gui.addFolder("LambertMaterial");
              // 设置材质颜色
              lambertFolder.addColor(lambertInfo, "baseColor").onChange((v) => {
                  console.log('baseColor:', v);
                  let color: Color = new Color();
                  color.setHex(v);
                  this.lambertMaterial.baseColor = color;
              });
      
              // 创建保存属性值对象
              const pdrInfo = {
                  baseColor: '#fff',
                  roughness: 0.01,
                  metallic: 0.01,
      
              };
      
              let hdrFolder = gui.addFolder("PDRMaterial");
              // 设置材质颜色
              hdrFolder.addColor(pdrInfo, "baseColor").onChange((v) => {
                  console.log('baseColor:', v);
                  let color: Color = new Color();
                  color.setHex(v);
                  this.hdrMaterial.baseColor = color;
              });
      
              // 材质粗糙程度
              hdrFolder.add(pdrInfo, "roughness", 0, 1).onChange((v) => {
                  console.log('roughness:', v);
                  this.hdrMaterial.roughness = v;
              });
      
              // 材质粗糙程度
              hdrFolder.add(pdrInfo, "metallic", 0, 1).onChange((v) => {
                  console.log('metallic:', v);
                  this.hdrMaterial.metallic = v;
              });
      
          }
      
      }
      

      运行效果

      718ac9eb-6100-49cb-b7a6-34446ab03a37-image.png

      小结

      继几何体后,引擎对常用材质做了封装,使用户可以不必自己编写shader,创建一个对应的类,设置参数就可以得到一个可用的材质,用来丰富物体。材质的种类还有一些,不过当前只发现了三种,期待后续会有更多的材质可以使用。
      作为3D新手,后续会不断的记录学习过程,期待与你一起学习一起飞!

      1 条回复 最后回复 回复 引用 0
      • First post
        Last post

      Recent Post

      • 目前可以预览demo了

        • 阅读更多
      • A

        这demo太卡了,我机器性能不算差,运行个demo cpu就将近100%

        • 阅读更多
      • A

        没有贴出app.vue的代码

        • 阅读更多
      • @aichangqing 可能是之前版本的cdn缓存没更新,可以清理本地缓存刷新再试一次

        • 阅读更多
      • 36e6af78-b023-4031-9b56-bd8713b44393-image.png

        已经是版本 113.0.5656.0(正式版本)canary (64 位)并且开启chrome://flags/#enable-unsafe-webgpu 为enable,为啥还不能预览demo

        • 阅读更多

      Copyright © 2022 Orillusion | Contact US