上一篇之后停了好久没写,但其实mini3d.js一直在进展,目前的内容够写很多篇了,但这样欠下的债就太多了。我开始思考我写这些文章的初衷,是对这个过程的记录以及思考的总结,而不是写WebGL教程。教程的问题是如果讲得很细非常花篇章,而讲得很简略又没有用处。所以本篇开始减少代码细节的描述,只重点讨论技术原理和设计思路。并且每一组文章都要紧扣着当前讨论的里程碑目标。
本篇对应代码请参考:mini3d.js

回顾

上一篇中,我们讨论了mini3d.js对静态模型的封装,提供了自定义顶点格式的支持,以及顶点属性和shader中attribute的绑定。模型为我们当前的目标-渲染一个立方体提供了数据,但是具体怎么渲染它需要shader的支持。那么什么是shader,shader具体做了啥,mini3d.js目前对shader提供了哪些封装和接口,是本篇要讨论的问题。

材质和Shader

WebGL的shader狭义上说是指一组运行在GPU上的小程序,vertex shaderfragment shader。其中VS的作用是输出裁剪空间的顶点和其他中间变量,FS的作用是计算片段的颜色。
但是光靠VS和FS往往还不够,WebGL还可以设置各种渲染状态,如混合的参数,并且某些渲染方式可能需要执行多次不同的渲染,称为多个PASS,并且某些引擎会提供VS/FS无法执行时的替代解决方案(fallback)。所有的这些综合起来可以在一个文件里面定义(或者通过代码组织),一些引擎叫做材质(Material),而另一些引擎可能就叫shader(例如unity)。材质只是一种渲染方案的模板,可能有多个物体使用同一个材质渲染,但是有不同的参数值(如diffuse贴图),这些具有不同参数值的材质在某些引擎里面叫做材质实例(Material Instance)。而unity中的材质其实是unity shader的实例,相当于前述引擎的Material Instance。虽然叫法不同,但是思路都很类似。mini3d.js也会实现材质系统,但是目前先把shader封装一下,因为shader本身也有一些细节。

WebGL shader的输入输出

WebGL的shader是GLSL ES语言的小程序,和OpenGL ES 2.x/3.x基本是通用的。我们看一下OpenGL ES 3.0的VS和FS的输入输出:


其中VS输入Attribute和Uniform (先忽略Sampler),输出Varying和gl_Position以及gl_PointSize
FS输入Varying, Uniform和Sampler,输出gl_FragColor (暂时没找到OpenGL ES 2.0的图,3.0中FS输出了一组Color先不用管它)
对于VS,输入的Attribute就来自我们提交的顶点数据,这个数据的准备工作已经在上一篇中做好了,并且已经和shader进行了绑定。这样执行VS时就可以对于模型的每个顶点执行一次VS,并且传入每个顶点的各个属性。但是为了执行这个绑定,shader必须提供Attribute的Location,所以这方面需要封装一下。 而Uniform输入需要调用相应的API,这也是需要封装的一个点,因为Uniform数据类型很多直接使用API调用不方便。至于Sampler其实也是uniform的一种。

Shader的封装

mini3d.js目前提供了Shader类,这个Shader其实对应了WebGL的一个shader program。目前提供了几个操作。

从shader源码创建shader program

create(vshader, fshader)方法输入参数为vs和fs的源码,目前的demo里面源码是直接写在js代码里面的字符串,下一个里程碑将从资源文件载入。create内部创建了一组shader对象并编译链接为一个program对象。这个是最基本的封装,避免了每次创建shader的繁琐操作。

获取Attribute的location

WebGL中使用gl.getAttribLocation(program, name)可以通过名字获取location。但是我们不是直接这么做,mini3d.js首先使用gl.getProgramParameter(this.program, gl.ACTIVE_ATTRIBUTES)gl.getActiveAttrib(program,index)获取所有的active的attributes的信息,这个信息是一个WebGLActiveInfo对象,包含name,size,type。对于attribute我们只使用name,然后通过name获取到location并存储在this._attributes = {}; // {[name:string]:number}中。这样做的好处是防止代码中写错name并且确保attribute是激活的。但由于我们需要将Mesh中的顶点属性和shader的attribute进行绑定,还是需要在代码中输入name,但是毕竟这儿做了个检查。另外这么做相当于空间换时间,将所有的attribute location缓存起来避免重复查询。以上操作封装在findoutAttributes方法中并在create中自动调用。使用者只需要使用getAttributeLocation(semantic)接口通过semantic获取location。注意参数为semantic,这个就是在Mesh中定义的顶点属性语义,通过这个semantic将顶点属性和shader的attribute联系起来。

关联semantic和Attribute name

上面说了我们使用semantic获取attribute location,因此我们需要知道semantic对应的attribute name。Shader类提供了接口mapAttributeSemantic(semantic, attribName)进行设置。这么做还是需要在代码中写shader中的名字,但是这给以后的升级提供了基础,比如像Unity那样使用CG就可以直接将semantic标记在shader的变量后面,这样就可以自动化了。当然mini3d.js大概率不会使用CG,毕竟那需要工具链的支持,但我有可能在材质定义中进行标记,这样代码中就不用出现name了,而标记和shader都包裹在材质文件中,避免信息分散维护。

设置Uniform

WebGL关于设置uniform的API有gl.uniform[1234][fi][v]()文档和.uniformMatrix[234]fv()文档
太多了吧,调用起来太麻烦,作为一个框架必须得管理这个复杂度。好在我们是javascript,没有问题,我们封装了一个方法Shader类的setUniform(name, value)。不管是什么类型的uniform,都用这个方法设置。例如:

//设置矩阵
shader.setUniform('u_mvpMatrix', mvpMatrix.elements);
//设置sampler
shader.setUniform('u_sampler', 0);
//设置vec3
shader.setUniform('u_LightColor', [1.0,1.0,1.0]);

后面两个暂时超纲了,里程碑1还没有。
这个setUniform做了所有的苦活累活,基本原理就是先获取shader中所有active的uniform的信息(在findoutUniforms中获取)。这个信息是一个如下的对象:

class UniformInfo{    constructor(name, location, type, size, isArray){        this.name = name;        this.location = location; //WebGLUniformLocation                       this.type = type;        this.size = size;           this.isArray = isArray;         }
}

然后在setUniform方法中会根据name所对应的info的类型调用不同的API进行设置。目前阶段还没支持所有的类型组合,先支持了一些常用的。

小结和展望

目前这个阶段对shader进行了简单的封装,降低调用的复杂度并且可以更方便的和模型进行绑定。mini3d.js没有像某些引擎那样写死所有常用的顶点属性然后定死了attribute的名字,而是通过semantic进行关联,这主要是为了扩展性,必须对于一个基于shader的渲染框架来说,让用户可以自由的写shader非常重要。虽然想达到Unity那样的成熟度很困难,但我们可以一步一步来。未来肯定会写材质系统,甚至shader graph这样方便技美创作的工具,但是到写工具的那一步就真的向一个引擎发展了,目前来说先实现基本目标吧,毕竟太多的事情想做。比如说提供一些常用的uniform,如各种矩阵,按照某个约定的名字在渲染前自动传入到shader中,再如提供一些公共方法方便写shader时调用,甚至像对于阴影的支持这种,都需要引擎提供可在shader中获取的数据。

从零开始手撸WebGL3D引擎5:Shader的封装相关推荐

  1. 从零开始手撸一个热修复框架

    1.前言 热修复原理,这个一直是这几年来很热门的话题,在项目中使用的话,也基本要么是阿里系或者腾讯系的开源框架.但是作为一个光会使用的程序员是远远不够的.这篇文章会从dex分包的原因,原理,热修复的由 ...

  2. umi脚手架搭建的项目_还在从零开始搭建项目?手撸了款快速开发脚手架!

    之前开源了一款项目骨架mall-tiny,完整继承了mall项目的整个技术栈.总感觉mall-tiny集成了太多中间件,过于复杂了.这次对其进行了简化和升级,使它成为了一款拥有完整权限管理功能的快速开 ...

  3. 1.从零开始手敲次时代游戏引擎(序言)

    大家好.我"正式"从事软件工程师这个职业已经快15年了.至于编程的历史则更长,有20余年了.记忆当中第一次编程的机器里只有ROM BASIC,用"*"打了个金字 ...

  4. 从零开始手敲次时代游戏引擎(目录)

    原文链接:https://zhuanlan.zhihu.com/c_119702958 目录 1.从零开始手敲次世代游戏引擎(序) 2.从零开始手敲次世代游戏引擎(HelloEngine) 3.从零开 ...

  5. ​.NET手撸2048小游戏

    前言 2048是一款益智小游戏,得益于其规则简单,又和 2的倍数有关,因此广为人知,特别是广受程序员的喜爱. 本文将再次使用我自制的"准游戏引擎" FlysEngine,从空白窗口 ...

  6. 手撸一款精美的水波气泡

    代码地址如下: http://www.demodashi.com/demo/13434.html Android自定义水波气泡 前言:公司在做的一个项目,要求在地图上以水波气泡的形式来显示站点,并且气 ...

  7. Unity URP 手撸一个自己的PBR材质

    嘿嘿,你能认出哪个是官方的lit shader,哪个是我手撸的PBRshader吗 . 然后就可以魔改成风格化的PBR拉 先占个坑,后面有空的话会梳理一遍URP的pbr流程和相关公式 最后一定要注意自 ...

  8. 耗时两周手撸了一个 RPC 轮子,是驴子是马拉出来遛遛

    手撸 RPC 轮子系列文章目录: 「从零开始造 RPC 轮子系列」01 我为什么要去造一个轮子? 「从零开始造 RPC 轮子系列」02 演示轮子,是驴是马拉出来遛遛] (TODO)「从零开始造 RPC ...

  9. 手撸架构,Kafka 面试42问

    技术栈 传送门 JAVA 基础 手撸架构,Java基础面试100问_vincent-CSDN博客 JAVA 集合 手撸架构,JAVA集合面试60问_vincent-CSDN博客 JVM 虚拟机 手撸架 ...

  10. 手撸架构,Redis面试41问

    技术栈 传送门 JAVA 基础 手撸架构,Java基础面试100问_vincent-CSDN博客 JAVA 集合 手撸架构,JAVA集合面试60问_vincent-CSDN博客 JVM 虚拟机 手撸架 ...

最新文章

  1. 利用BP神经网络教计算机识别语音特征信号(代码部分SS)
  2. 安卓中实现两端对齐,中间fill_parent的方法
  3. BZOJ1083: [SCOI2005]繁忙的都市
  4. Typecho给文章设置永久链接
  5. jQuery 对HTML的操作(二)
  6. java实现遍历树形菜单方法——OpenSessionView实现
  7. 推荐几个练习听力不错的国外网站
  8. amd cpu不能在cmd环境下运行java代码_如何在Windows10中配置java的JDK环境
  9. 弹性计算平台技术:云服务器“安全”“稳定”“弹性”的基石
  10. Python学习笔记(5):Python如何忽略warning的输出
  11. 图片人脸检测——OpenCV版(二)
  12. linux系统ftp优化,Linux vsftp 部署优化
  13. 巧用负载均衡 解决数据中心三大困惑
  14. (33)FPGA面试题附加约束的作用
  15. nginx限流方案的实现(三种方式)
  16. 热门NPM库 “coa” 和“rc” 接连遭劫持,影响全球的 React 管道
  17. fw313r手机登录_迅捷(FAST)fw313r路由器手机设置教程 | 192路由网
  18. 关于水晶易表的简介及水晶易表安装初识
  19. Netlog的数据库及LAMP架构
  20. DEBUG指示灯详细说明

热门文章

  1. 机器学习中的数学(下)
  2. Linux之wubi输入法
  3. Hadoop生态系统概述
  4. Cadence Allegro 导出Netin(back anno.)报告详解
  5. cmd无敌加密安全代码
  6. 企业网站初创,如何做好网络营销优化?
  7. LeetCode Top 100 Liked Questions 226. Maximal Square (Java版; Medium)
  8. 云原生 envoy xDS 动态配置 java控制平面开发 支持restful grpc实现 EDS 动态endpoint配置
  9. 聊聊区块链--如何投资数字货币
  10. 南开大学计算机控制工程学院院长,南开大学计算机与控制工程学院研究生导师简介-任博...