JavaScript OpenGL接口再设计
Web OpenGL封装
- 简介
- 上下文GLContext
- 基类GLObject
- 着色器GLShader
- 着色器程序GLProgram
- 着色器程序属性GLAttribute、GLUniform
- 数据缓存GLBuffer
- GLTexture和GLFrameBuffer
- html标签
- Canvas
- vertex shader
- fragment shader
- iChannels
- 主程序ShaderToyGame
- 主函数
简介
将OpenGL提供的库函数接口进行一层封装。
上下文GLContext
OpenGL自身是一个巨大的状态机(State Machine):一系列的变量描述OpenGL此刻应当如何运行。OpenGL的状态通常被称为OpenGL上下文(Context)。我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。
<!--GLContext单例GL上下文对象-->
<script type="text/javascript">class GLContext {static width = 0static height = 0constructor() {this.gl = null}static getWebGL() {if (!this.gl) {// 获取html中的glCanvaslet canvas = document.getElementById('glCanvas') //.transferControlToOffscreen()GLContext.width = canvas.widthGLContext.height = canvas.height// 获取glCanvas中的gl上下文this.gl = canvas.getContext('webgl2')}return this.gl}}
</script>
基类GLObject
由于大部分GL相关的封装类,都有着共同的方法,因此写一个基类。
<!--GLObjectGL对象的基类-->
<script type="text/javascript">class GLObject {constructor() {this.gl = GLContext.getWebGL()}bind() {}unbind() {}}
</script>
着色器GLShader
OpenGL在创建一个“笔刷”的时候需要先创建vertex shader
和fragment shader
。
<!--用于创建:顶点着色器 or 片元着色器GLShaderGL着色器-->
<script type="text/javascript">class GLShader extends GLObject {/*** @param sourceCode 元素id、shaderCode字符串、元素对象* @param type shader的类型*/constructor(sourceCode, type) {super()if (sourceCode == undefined || type.constructor != Number) {// sourceCode为空 或者 type着色器类型不是数字throw "sourceCode is undefined or type not number. \n";} else if (sourceCode.constructor == String) {// sourceCode本身是一个字符串, 要么是shader源码, 要么是标签的idif (sourceCode[0] == '#') {// sourceCode是一个idsourceCode = $(sourceCode)[0].innerHTML}} else if (sourceCode.constructor == HTMLScriptElement) {// sourceCode是一个元素对象sourceCode = sourceCode.innerHTML} else {// 其他情况直接错误throw "Create Shader Error. \n";}// 创建对应type的着色器this.id = GLShader.createShader(this.gl, sourceCode, type)}/*** @param gl gl上下文* @param sourceCode shaderCode字符串* @param type shader的类型*/static createShader(gl, sourceCode, type) {// 创建对应type的shader句柄let shader = gl.createShader(type);// 绑定sourceCode和typegl.shaderSource(shader, sourceCode);// 编译shadergl.compileShader(shader);// 检查错误if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {// 获取shader编译信息let info = gl.getShaderInfoLog(shader);// 释放shadergl.deleteShader(shader)// 抛出错误throw "Could not compile WebGL program. \n" + (type == gl.VERTEX_SHADER ? "vertex" : "fragment") + " shader error. \n" + info;}// 创建完成return shader;}delete() {// 释放shaderthis.gl.deleteShader(this.id)}}
</script>
着色器程序GLProgram
真正的“笔刷(Program)”,需要传入vertex shader和fragment shader,才能成为一个program
。
<!--用于创建:笔刷(着色器程序)GLProgram着色器程序-->
<script type="text/javascript">class GLProgram extends GLObject {/*** @param vsCode vs元素id、vsCode字符串、元素对象(顶点着色器)* @param fsCode fs元素id、fsCode字符串、元素对象(片元着色器)*/constructor(vsCode, fsCode) {super()if (vsCode == undefined || fsCode == undefined) {// vsCode为空 或者 fsCode为空throw "vsCode is undefined or fsCode is undefined. \n";}// gl句柄let gl = this.gl// 创建vs和fslet vertexShader = new GLShader(vsCode, gl.VERTEX_SHADER)let fragmentShader = new GLShader(fsCode, gl.FRAGMENT_SHADER)// 创建shader programlet program = gl.createProgram()// 将vs、fs与program绑定gl.attachShader(program, vertexShader.id)gl.attachShader(program, fragmentShader.id)// 链接shader programgl.linkProgram(program)// 检查错误if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {// 获取program链接信息let info = gl.getProgramInfoLog(program)// 释放programthis.delete()// 抛出错误throw 'WebGL program compile error. \n' + info}// 保存vs、fs、program的句柄this.vertexShader = vertexShaderthis.fragmentShader = fragmentShaderthis.id = program}/*** 使用program脚本*/use() {this.gl.useProgram(this.id)}/*** 删除program脚本,释放内存*/delete() {this.vertexShader.delete()this.fragmentShader.delete()this.gl.deleteProgram(this.id)}}
</script>
着色器程序属性GLAttribute、GLUniform
在use program
后,program还可能需要颜料(attribute/uniform)
,为此包装对应的attribute和uniform
。
<!--GLAtributeGLUniform-->
<script type="text/javascript">class GLAttribute extends GLObject {/*** @param program 着色器对象、着色器id* @param name 属性名*/constructor(program, name) {super()if (program == undefined || name == undefined) {// program为空 或者 name为空throw "program is undefined or name is undefined. \n";} else if (program.constructor != GLProgram) {// program不是GLProgram类型的throw "typeof(" + program + ") != GLProgram. \n";}// program idprogram = program.id// gl句柄let gl = this.gl// 获取program中name的idlet id = gl.getAttribLocation(program, name)if (id < 0) {// 在gl中, 获取到的id不可能小于0 (小于0说明name错误)throw "attribute " + name + " id < 0. \n";}// 保存idthis.id = id}/*** 绑定Attribute*/bind() {this.gl.enableVertexAttribArray(this.id)}/*** 解绑定Attribute*/unbind() {this.gl.disableVertexAttribArray(this.id)}/*** 设置Attribute属性*/vertexAttribPointer(size, type, normalized, stride, offset) {this.gl.vertexAttribPointer(this.id, size, type, normalized, stride, offset)}}class GLUniform extends GLObject {/*** @param program 着色器对象、着色器id* @param name 属性名*/constructor(program, name) {super()if (program == undefined || name == undefined) {// program为空 或者 name为空throw "program is undefined or name is undefined \n";} else if (program.constructor != GLProgram) {// program不是GLProgram类型的throw "typeof(" + program + ") != GLProgram. \n";}// program idprogram = program.id// gl句柄let gl = this.gl// 获取program中name的idlet id = gl.getUniformLocation(program, name)if (id < 0) {// 在gl中, 获取到的id不可能小于0 (小于0说明name错误)throw "uniform " + name + " id < 0 \n";}// 保存idthis.id = id}uniform1f(v0) {this.gl.uniform1f(this.id, v0)}uniform1fv(value) {this.gl.uniform1fv(this.id, value)}uniform1i(v0) {this.gl.uniform1i(this.id, v0)}uniform1iv(value) {this.gl.uniform1iv(this.id, v0)}uniform2f(v0, v1) {this.gl.uniform2f(this.id, v0, v1)}uniform2fv(value) {this.gl.uniform2fv(this.id, value)}uniform2i(v0, v1) {this.gl.uniform2i(this.id, v0, v1)}uniform2iv(value) {this.gl.uniform2iv(this.id, value)}uniform3f(v0, v1, v2) {this.gl.uniform3f(this.id, v0, v1, v2)}uniform3fv(value) {this.gl.uniform3fv(this.id, value)}uniform3i(v0, v1, v2) {this.gl.uniform3i(this.id, v0, v1, v2)}uniform3iv(value) {this.gl.uniform3iv(this.id, value)}uniform4f(v0, v1, v2, v3) {this.gl.uniform4f(this.id, v0, v1, v2, v3)}uniform4fv(value) {this.gl.uniform4fv(this.id, value)}uniform4i(v0, v1, v2, v3) {this.gl.uniform4i(this.id, v0, v1, v2, v3)}uniform4iv(value) {this.gl.uniform4iv(this.id, value)}uniformMatrix2fv(location, transpose, value) {this.gl.uniformMatrix2fv(this.id, transpose, value)}uniformMatrix3fv(location, transpose, value) {this.gl.uniformMatrix2fv(this.id, transpose, value)}uniformMatrix4fv(location, transpose, value) {this.gl.uniformMatrix2fv(this.id, transpose, value)}}
</script>
数据缓存GLBuffer
OpenGL在绘制时需要知道画在**哪里(vertex/uv)
**也就是buffer
。
<!--GLBuffer-->
<script type="text/javascript">class GLBuffer extends GLObject {/*** @param buffer 数据信息*/constructor(buffer) {super()if (buffer == undefined) {// buffer为空throw "buffer is undefined \n";}// gl句柄let gl = this.gl// vbolet vbo = gl.createBuffer()// 绑定vbogl.bindBuffer(gl.ARRAY_BUFFER, vbo)// vbo数据设置gl.bufferData(gl.ARRAY_BUFFER, buffer, gl.STATIC_DRAW)// 保存vbo idthis.id = vbo}/*** 绑定vbo*/bind() {this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.id)}/*** 释放vbo*/delete() {this.gl.deleteBuffer(this.id)}}
</script>
GLTexture和GLFrameBuffer
已经准备好笔刷、颜料、画什么东西,最后还差画在哪里(fbo)
。当然我们还可以直接拿一张图贴到fbo上,也就是直接拿一张**贴纸(texture)
**画在fbo
上。
<!--GLTextureGLFramebuffer-->
<script type="text/javascript">class GLTexture extends GLObject {/*** @param imageUrl 图片*/constructor(imageUrl) {super()if (imageUrl == undefined) {// 路径为空throw "imageUrl is undefined. \n";} else if (imageUrl.constructor == String) {// 路径不为空if (imageUrl[0] == '#') {// 获取srcimageUrl = $($(imageUrl)[0]).attr("src")}}// gl句柄let gl = this.gl// this句柄let that = this// 创建imagelet image = new Image()// 服务器不支持跨域访问的话就会被拦截image.crossOrigin = "Anonymous"// image加载成功后的回调 (ps: 这里可能会出现加载的时序问题)image.onload = function() {// 创建texture idlet textureId = gl.createTexture()// 绑定texturegl.bindTexture(gl.TEXTURE_2D, textureId)// 设置texture属性gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR)gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR)gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE)gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE)// 设置像素存储模式gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)// 设置texture格式gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, image)if (textureId <= 0) {// 创建成功的texture id必然大于0, 小于说明失败, 因此释放texturegl.deleteTexture(textureId)// 抛出错误throw "texture create error. \n";}// 保存idthat.id = textureId// 解绑定 (ps: 最好做, 为了保持一致, 这个原因是因为后面可能会有绘制也会用到gl.bindTexture(gl.TEXTURE_2D, textureId))gl.bindTexture(gl.TEXTURE_2D, null)}// image加载失败后的回调image.onerror = function() {alert("get image error")}// 设置image的src, 设置后将加载image.src = imageUrl}/*** 绑定texture*/bind() {this.gl.bindTexture(this.gl.TEXTURE_2D, this.id)}/*** 释放texture*/delete() {this.gl.deleteTexture(this.id)}}class GLFramebuffer extends GLObject {/*** 一般来说, FBO是framebuffer和texture绑定* @param texture 纹理*/constructor(texture) {// 创建fbolet framebuffer = gl.createFramebuffer()// 绑定fbogl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)// 保存texture对象if (texture == undefined) {this.texture = undefined} else if (texture.constructor == GLTexture) {this.texture = texture} else if (texture.constructor == String) {this.texture = new GLTexture(texture)}// 将fbo和texture绑定if (texture != undefined) {gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.texture.id, 0)}// 保存fbo idthis.id = framebuffer}/*** 绑定fbo*/bind() {this.gl.bindFramebuffer(this.FRAMEBUFFER, this.id)}/** * 释放fbo*/delete() {this.texture.delete()this.deleteFramebuffer(this.id)}}
</script>
html标签
Canvas
画布,WebGL需要先创建一个Canvas,通过Canvas拿到WebGLContext。
<div style="display: flex;"><canvas id="glCanvas" width="680" height="640">你的浏览器似乎不支持或者禁用了HTML5 <code><canvas></code> 元素.</canvas>
</div>
vertex shader
顶点着色器的代码。
<!-- shader脚本 -->
<script id="shader-vs" type="x-shader/x-vertex">attribute vec4 iPosition;attribute vec2 iTexcoord;uniform float iTime;varying highp vec2 vTexcoord;void main(void) {gl_Position = iPosition;vTexcoord = iTexcoord;}
</script>
fragment shader
片段着色器的代码。
<script id="shader-fs" type="x-shader/x-fragment">uniform sampler2D iChannel0;uniform highp float iTime;varying highp vec2 vTexcoord;void main(void) {gl_FragColor = texture2D(iChannel0, vTexcoord);}
</script>
iChannels
纹理图片。
<img id="iChannel0" src="data:images/src0.jpg" class="hidden"/>
<img id="iChannel1" src="data:images/src1.jpg" class="hidden"/>
<!-- ..................<img id="iChannel7" src="data:images/src7.jpg" class="hidden"/>
-->
主程序ShaderToyGame
这只是一个调用以上封装好GL接口的实例
。
<script type="text/javascript">class ShaderToyGame {constructor() {let gl = GLContext.getWebGL()// 确认WebGL支持性if (!gl) {alert("无法初始化WebGL,你的浏览器、操作系统或硬件等可能不支持WebGL。")return}// 使用完全不透明的黑色清除所有图像gl.clearColor(0.0, 1.0, 0.0, 1.0)// 用上面指定的颜色清除缓冲区gl.clear(gl.COLOR_BUFFER_BIT)// 设置视口gl.viewport(0, 0, GLContext.width, GLContext.height)}run(isRun) {if (this.isRun == isRun) {// 状态一样, 直接返回return;}// 是否运行, false会停止运行this.isRun = isRun;if (isRun) {// 开启requestrequestAnimationFrame(renderGL)}}renderGL(now) {// gl上下文let gl = GLContext.getWebGL()// 使用programthis.program.use()// 绑定和输入顶点数据this.verteBuffer.bind()this.vertexPosition.bind()this.vertexPosition.vertexAttribPointer(3, gl.FLOAT, false, 0, 0)// 绑定和输入纹理数据this.texcoordBuffer.bind()this.texcoordPosition.bind()this.texcoordPosition.vertexAttribPointer(2, gl.FLOAT, false, 0, 0)// 输入时间数据this.timeUniform.uniform1f(now * 0.001)// 输入纹理数据for(let i = 0; i < this.maxSize; i++) {if(this.textures[i]) {gl.activeTexture(gl.TEXTURE0+i)this.textures[i].bind()this.iChannels[i].uniform1i(i)}}// 绘制gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4)// 递归调用if(this.isRun)requestAnimationFrame(renderGL)}render() {let gl = GLContext.getWebGL()// 顶点坐标let vertices = [1.0, 1.0, 0.0, -1.0, 1.0, 0.0,1.0, -1.0, 0.0, -1.0, -1.0, 0.0]// 纹理坐标let texcoord = [1.0, 1.0,0.0, 1.0,1.0, 0.0,0.0, 0.0]// 顶点bufferthis.verteBuffer = new GLBuffer(new Float32Array(vertices))// 纹理bufferthis.texcoordBuffer = new GLBuffer(new Float32Array(texcoord))// 着色器programlet program = new GLProgram("#shader-vs", "#shader-fs")this.program = program// 顶点属性let vertexPosition = new GLAttribute(program, "iPosition")// 纹理属性let texcoordPosition = new GLAttribute(program, "iTexcoord")// 时间属性let timeUniform = new GLUniform(program, "iTime")// 纹理数组let iChannels = []this.textures = []// texture最大数量this.maxSize = 8// 创建iChannels属性数组和texture数组for(let i = 0; i < this.maxSize; i++) {let iChannel = new GLUniform(program, 'iChannel'+i)let texture = undefinedif(iChannel.id) { texture = new GLTexture('#iChannel'+i) }iChannels.push(iChannel)this.textures.push(texture)}// 开始渲染run(true);}destory() {this.isRun = falsethis.program.delete()this.verteBuffer.delete()this.texcoordBuffer.delete()for(let i = 0; i < this.maxSize; i++) {if(this.textures[i]) { this.textures[i].delete() }}}}
</script>
主函数
<script type="text/javascript">// 主函数function main() {let stg = new ShaderToyGame()stg.render()}main()
</script>
JavaScript OpenGL接口再设计相关推荐
- 如何在qml中使用opengl接口进行渲染
在QWidget中,可以使用QOpenglWidget调用opengl接口进行渲染,因为QWidget大部分控件都是依赖于平台的(cpu绘制),所以我们调用opengl的接口时不需要考虑opengl的 ...
- 调试接口用的HTML,javascript调试接口
最近在学习 JS 的面向对象实现,看「Javascript 设计模式」看不明白 JS 关于接口是怎么实现的. 最近做的一个小例子如下图: 下面有一段在JS中使用接口的代码,不知道是不是你在<Ja ...
- JavaScript实现接口的三种经典方式
1 /* 2 接口:提供一种说明一个对象应该有哪些方法的手段 3 js中有三种方式实现接口: 4 1 注释描述接口 5 2 属性检测接口 6 3 鸭式辨型接口 7 */ 8 9 /* 10 1 注释描 ...
- invalid signature_php,javascript - 微信接口调用一直是 invalid signature
最近在开发过程中用到微信的分享接口,但是按照他的文档来生成签名时,却一直出现invalid signature 的情况. 按照他提供的检测工具,检测出来的结果是正确的. 我这里获取签名的方法是在静态页 ...
- 图形程序接口知多少 | OpenGL、OpenCL、Vulkan、OpenGL ES、WebGL、Metal、Directx
计算机图形程序接口 | Graphics API 对于普通的电脑玩家来说这是一个不重要的东西,但对于游戏开发.玩游戏做三维的人来说,这部分知识可能会常常被提及,尤其是涉及到游戏相关的.计算机图形程序接 ...
- 深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP
前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第4篇,接口隔离原则ISP(The Interface Segregation Principle). 英文原文:htt ...
- JavaScript是否具有接口类型(例如Java的“接口”)?
本文翻译自:Does JavaScript have the interface type (such as Java's 'interface')? I'm learning how to make ...
- JavaScript 游戏开发包-收集
基于WebGL的游戏引擎PlayCanvas 一般来讲,游戏开发与web应用完全是两码事.但先试试,游戏世界的很多工具都可以被用于在网站中增加华丽界面.PlayCanvas就是一个基于WebGL的游戏 ...
- 关于WebGL,Three.js,OpenGL,Direct3D,CSS3D,GPU
OpenGL OpenGL(英语:Open Graphics Library,译名:开放图形库或者"开放式图形库")是用于渲染2D.3D矢量图形的跨语言.跨平台的应用程序编程接口( ...
最新文章
- 基于React与Redux的留言墙的实现
- BZOJ3559 : [Ctsc2014]图的分割
- python 参数解析_python的函数对参数解析分析
- ShineTime - 带有 CSS3 闪亮特效的缩略图相册
- java file构造方法_Java中FileOutputStream类的常用方法
- c语言里,关于宏定义的使用
- matlab标签背景透明,ROI透明背景(matlab)
- 在tomcat文件夹下启动war项目
- 树莓派红外避障小车python_制作树莓派wifi遥控和自动避障小车
- django基础知识总结
- HTML---表格table标签中thead、tbody、tfoot的作用
- 股票python量化交易002-常见量化指标(基本面)
- ICLR 2022最佳论文解读
- jupyterLab 如何修改字体大小
- 解决Word文章表格中无法自动换页的问题,表格结尾处像是被下一页覆盖掉了
- 课余或者业余学习python,可以嘛?
- 为什么要学Markdown?有什么用?
- html腾讯视频组件,3.腾讯视频组件
- 重装系统Win7步骤
- kaggle 共享单车项目数据分析和单车租赁数预测