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 shaderfragment 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>&lt;canvas&gt;</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接口再设计相关推荐

  1. 如何在qml中使用opengl接口进行渲染

    在QWidget中,可以使用QOpenglWidget调用opengl接口进行渲染,因为QWidget大部分控件都是依赖于平台的(cpu绘制),所以我们调用opengl的接口时不需要考虑opengl的 ...

  2. 调试接口用的HTML,javascript调试接口

    最近在学习 JS 的面向对象实现,看「Javascript 设计模式」看不明白 JS 关于接口是怎么实现的. 最近做的一个小例子如下图: 下面有一段在JS中使用接口的代码,不知道是不是你在<Ja ...

  3. JavaScript实现接口的三种经典方式

    1 /* 2 接口:提供一种说明一个对象应该有哪些方法的手段 3 js中有三种方式实现接口: 4 1 注释描述接口 5 2 属性检测接口 6 3 鸭式辨型接口 7 */ 8 9 /* 10 1 注释描 ...

  4. invalid signature_php,javascript - 微信接口调用一直是 invalid signature

    最近在开发过程中用到微信的分享接口,但是按照他的文档来生成签名时,却一直出现invalid signature 的情况. 按照他提供的检测工具,检测出来的结果是正确的. 我这里获取签名的方法是在静态页 ...

  5. 图形程序接口知多少 | OpenGL、OpenCL、Vulkan、OpenGL ES、WebGL、Metal、Directx

    计算机图形程序接口 | Graphics API 对于普通的电脑玩家来说这是一个不重要的东西,但对于游戏开发.玩游戏做三维的人来说,这部分知识可能会常常被提及,尤其是涉及到游戏相关的.计算机图形程序接 ...

  6. 深入理解JavaScript系列(21):S.O.L.I.D五大原则之接口隔离原则ISP

    前言 本章我们要讲解的是S.O.L.I.D五大原则JavaScript语言实现的第4篇,接口隔离原则ISP(The Interface Segregation Principle). 英文原文:htt ...

  7. JavaScript是否具有接口类型(例如Java的“接口”)?

    本文翻译自:Does JavaScript have the interface type (such as Java's 'interface')? I'm learning how to make ...

  8. JavaScript 游戏开发包-收集

    基于WebGL的游戏引擎PlayCanvas 一般来讲,游戏开发与web应用完全是两码事.但先试试,游戏世界的很多工具都可以被用于在网站中增加华丽界面.PlayCanvas就是一个基于WebGL的游戏 ...

  9. 关于WebGL,Three.js,OpenGL,Direct3D,CSS3D,GPU

    OpenGL OpenGL(英语:Open Graphics Library,译名:开放图形库或者"开放式图形库")是用于渲染2D.3D矢量图形的跨语言.跨平台的应用程序编程接口( ...

最新文章

  1. 基于React与Redux的留言墙的实现
  2. BZOJ3559 : [Ctsc2014]图的分割
  3. python 参数解析_python的函数对参数解析分析
  4. ShineTime - 带有 CSS3 闪亮特效的缩略图相册
  5. java file构造方法_Java中FileOutputStream类的常用方法
  6. c语言里,关于宏定义的使用
  7. matlab标签背景透明,ROI透明背景(matlab)
  8. 在tomcat文件夹下启动war项目
  9. 树莓派红外避障小车python_制作树莓派wifi遥控和自动避障小车
  10. django基础知识总结
  11. HTML---表格table标签中thead、tbody、tfoot的作用
  12. 股票python量化交易002-常见量化指标(基本面)
  13. ICLR 2022最佳论文解读
  14. jupyterLab 如何修改字体大小
  15. 解决Word文章表格中无法自动换页的问题,表格结尾处像是被下一页覆盖掉了
  16. 课余或者业余学习python,可以嘛?
  17. 为什么要学Markdown?有什么用?
  18. html腾讯视频组件,3.腾讯视频组件
  19. 重装系统Win7步骤
  20. kaggle 共享单车项目数据分析和单车租赁数预测

热门文章

  1. 关于SQL Server 数据库归档的一些思考和改进
  2. uvalive 4795 Paperweight
  3. 深入了解回调函数Java
  4. PHP 汉字转拼音(首拼音,所有拼音)
  5. Overload 和Override 的区别
  6. vaniglia 源码学习 (六)
  7. 新个人项目-- 拼图游戏
  8. NSIS修改开始菜单中图标
  9. velocity 继续之 语法学习篇
  10. 插入排序算法C++代码实现