搭建WebGL开发环境

前言

本篇文章介绍如何搭建WebGL开发环境

WebGL

WebGL的技术规范继承自免费和开源的OpenGL ES标准,从某种意义上说,WebGL就是Web版的OpenGL ES,而OpenGL ES是从OpenGL中派生出来的。他们的应用环境有区别,一般来说:

  • OpenGL:一般应用于桌面级别的三维图形渲染,同类的还有微软的DirectX和苹果公司的Metal技术
  • OpenGL ES:OpenGL的子集,删除了很多用处不大的接口和特性,但仍然能进行三维渲染,应用于智能手机、嵌入式计算机、家用游戏机等
  • WebGL:从OpenGL ES派生出来的。需要浏览器内核的支持。WebGL广泛应用于所有的网页渲染。因为需要运行在浏览器上,所以浏览器在背后做了很多工作,包括跨平台的特性。

版本

既然OpenGL、OpenGL ES和WebGL存在继承关系,那必然涉及到版本信息
先看一下OpenGL的版本信息:

  • OpenGL 1.5:这个版本的OpenGL不支持着色器编程,采用固定渲染管线
  • OpenGL 2.0:这个版本加入GLSL着色器编程语言
  • OpenGL 3.3:2010年发布
  • OpenGL 4.3:2013年发布

看一下OpenGL ES的版本信息:

  • OpenGL ES 1.1:OpenGL 1.5的子集,应用范围有限
  • OpenGL ES 2.0:OpenGL 2.0的子集,应用最广泛的版本
  • OpenGL ES 3.0:OpenGL 3.3的子集,增加了很多新功能,包括:
    • 遮挡查询
    • 变缓反馈(Transform Feedback)
    • 实例渲染(Instanced Rendering)
    • 四个或更多渲染目标支持
    • 着色语言全面支持整数和32位浮点操作
    • 纹理功能大幅增强,支持浮点纹理、3D纹理、深度纹理、顶点纹理、NPOT纹理、R/RG单双通道纹理、不可变纹理、2D阵列纹理、无二次幂限制纹理、阴影对比、调配(swizzle)、LOD与mip level clamps、无缝立方体贴图、采样对象、纹理MSAA抗锯齿渲染器
    • 一系列广泛的精确尺寸纹理和渲染缓冲格式

看一下WebGL的版本信息:

  • WebGL 1.0:基于OpenGL ES 2.0,并提供了3D图形的API,它使用HTML5Canvas并允许利用文档对象模型接口。
  • WebGL 2.0:WebGL 2.0基于OpenGL ES 3.0,确保了提供许多选择性的WebGL 1.0扩展,并引入新的API。可利用部分Javascript实现自动存储器管理

WebGL程序组成

一个最简单的WebGL程序之需要一个html文件和几个js文件就可以了,下面详细介绍

html部分

html主要是为了放置canvas元素,因为WebGL的渲染都是在canvas进行的。
下面是一个html部分:

<!DOCTYPE html>
<html lang="cn">
<head>
    <meta charset="utf-8">
    <title>wgl render ins</title>
</head>
<body onload="main()">
    <canvas id="wglCanvas" width="960" height="540">
        当前浏览器不支持canvas
    </canvas>
	<script src="sk_init.js"></script>
	<script src="sk_shader.js"></script>
	<script src="sk_utils.js"></script>
</body>
</html>

这个简单的不能再简单的html就已经够用了。

上面的html我们命名为main.html。从上面的html中,可以发现我们需要一个main函数,这个函数一般来说在js中实现,这也是我们WebGL程序正式开始的入口函数,我们添加一个js文件sk_init.js,并实现该函数,当然我们也需要在html中加入引用js的代码,为了保持代码的简洁,已经在上面的html中加入了该代码。

js部分

下面是sk_init.js的代码:

var gl;
function main()
{
    var canvas = document.getElementById("wglCanvas");
    gl = create3DContext(canvas);
    if(!gl)
    {
        console.log("获取webgl渲染上下文失败");
        return;
    }
    gl.clearColor(0.0,0.0,0.0,1.0);
    gl.clear(gl.COLOR_BUFFER_BIT);
}

var create3DContext = function(canvas) {
	var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
	var context = null;
	for (var ii = 0; ii < names.length; ++ii) {
		try {
			context = canvas.getContext(names[ii]);
		} catch(e) {}
		if (context) {
			break;
		}
	}
	return context;
}

上面这个代码分为几部分:

  • 获取canvas对象
  • 获取webgl上下文
  • 设置canvas画布背景色
  • 使用背景色清空绘图区

虽然代码很简单,但是这几步是所有webgl程序都需要做的事情,当前main.html已经可以运行了。不过只会显示一个黑色背景。下面我们开始加入些东西

shader

想编写一个真正可以使用的webgl程序,首先需要写shader,由于本篇文章我们着重介绍WebGL框架,所以我们只是简单说明一下,关于shader语法,会有专门文章介绍。

  • WebGL程序的shader一般有两个,顶点着色器片元着色器
  • 着色器是以字符串的形式嵌入到js文件中的

下面是两个简单的着色器代码,我们创建一个sk_shader.js存放这部分代码:
顶点着色器:

var VERTEX_SHADER=`
attribute vec4 a_Position;
uniform float u_PointSize;
void main() {
  gl_Position = a_Position;
  gl_PointSize = u_PointSize;
}`;

片元着色器:

var FRAG_SHADER=`
void main() {
  gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}`;

初始化着色器

创建好了着色器以后,下一步就要初始化着色器了

加载着色器代码

我们新建一个sk_util.js文件,里面封装一些公共的方法,首先我们要添加的方法就是加载着色器代码的方法:

// gl: webgl上下文
// type: 着色器类型
// source: 着色器代码
function loadShader(gl, type, source) {
  var shader = gl.createShader(type);
  if (shader == null) {
    console.log('unable to create shader');
    return null;
  }
  gl.shaderSource(shader, source);
  gl.compileShader(shader);
  var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  if (!compiled) {
    var error = gl.getShaderInfoLog(shader);
    console.log('Failed to compile shader: ' + error);
    gl.deleteShader(shader);
    return null;
  }
  return shader;
}

加载一个着色器需要以下步骤:

  • 创建着色器对象:使用gl.createShader接口,参数为着色器类型,着色器类型一般有两类:
    • gl.VERTEX_SHADER:顶点着色器
    • gl.FRAGMENT_SHADER:片元着色器
  • 加载着色器代码:使用gl.shaderSource接口,第一个参数为第一步创建的着色器对象,第二个参数为着色器的字符串表示
  • 编译着色器代码:使用gl.compileShader接口,参数为第一步创建的着色器对象
  • 获取着色器编译状态,如果编译失败,获取错误信息

创建着色器程序

下面是创建着色器程序的代码:

function createProgram(gl, vshader, fshader) {
  var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
  var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
  if (!vertexShader || !fragmentShader) {
    return null;
  }
  var program = gl.createProgram();
  if (!program) {
    return null;
  }
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);
  var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
  if (!linked) {
    var error = gl.getProgramInfoLog(program);
    console.log('Failed to link program: ' + error);
    gl.deleteProgram(program);
    gl.deleteShader(fragmentShader);
    gl.deleteShader(vertexShader);
    return null;
  }
  return program;
}

创建着色器程序的步骤如下:

  • 创建着色器程序:使用gl.createProgram接口,返回着色器程序对象
  • 关联着色器对象:使用gl.attachShader接口,第一个参数是第一步创建的着色器程序对象,第二个参数是关联的着色器对象
  • 链接着色器程序:使用gl.linkProgram接口链接程序,参数是第一步创建的着色器程序对象
  • 获取着色器程序链接状态,如果链接失败,获取错误信息

使用着色器对象

  • 使用gl.useProgram接口使用我们上一步创建的着色器程序对象。
  • 将着色器程序对象赋给gl.program,方便我们后面传递数据的时候使用

代码如下:

function initShaders(gl, vshader, fshader) {
    var program = createProgram(gl, vshader, fshader);
    if (!program) {
      console.log('Failed to create program');
      return false;
    }
    gl.useProgram(program);
    gl.program = program;
    return true;
}

经过这三步以后,着色器程序已经初始化完成了,接下来就是向着色器传递数据

传递数据

所谓的传递数据就是将内存的数据传递给显存
一般情况下shader中使用的数据有三种:

  • 使用attribute标记的数据,这个也叫做顶点数据,就是每个顶点都会有自己的数值
  • 使用uniform标记的数据,这个也叫做统一数据,就是所有顶点共用的数据
  • 使用varying标记的数据,这个是在着色器之间传递的数据

获取shader的数据标记

  • 使用gl.getAttribLocation接口获取attribute数据的标记,第一个参数是着色器程序对象,第二个参数是数据在shader中的名称的字符串表示
  • 使用gl.getUniformLocation接口获取uniform数据的标记,第一个参数是着色器程序对象,第二个参数是数据在shader中的名称的字符串表示

下面我们获取前面着色器程序的a_Position和u_PointSize的标记:

var u_PointSize = gl.getUniformLocation(gl.program, "u_PointSize");
if (u_PointSize < 0) {
	console.log('Failed to get the storage location of u_PointSize');
	return -1;
}
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
	console.log('Failed to get the storage location of a_Position');
	return -1;
}

从内存传递attribute数据到显存

  • 创建缓冲区,使用gl.createBuffer()创建缓冲区
  • 绑定缓冲区,使用gl.bindBuffer接口绑定缓冲区,第一个参数是缓冲区类型,这里我们使用gl.ARRAY_BUFFER,第二个参数是上一步创建的缓冲区
  • 传递数据,使用gl.bufferData接口传递数据,第一参数是缓冲区类型,这里我们使用gl.ARRAY_BUFFER,第二个参数是内存数据指针,第三个参数是数据的使用方式,这里我们使用gl.STATIC_DRAW
    代码如下:
var verticesTexCoords = new Float32Array([
    -0.5, 0.5, 0.0, 1.0,
    -0.5, -0.5, 0.0, 1.0,
    0.5, 0.5, 0.0, 1.0,
    0.5, -0.5, 0.0, 1.0,    
  ]);
var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
var vertexTexCoordBuffer = gl.createBuffer();
if (!vertexTexCoordBuffer) {
	console.log('Failed to create the buffer object');
	return -1;
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

传递uniform数据到显存

使用gl.uniform1f接口传递单个float数值到显存,在当前程序中用来传递u_PointSize的值
代码如下:

gl.uniform1f(u_PointSize, 10);

绑定缓冲区与attribute顶点属性

  • 使用gl.vertexAttribPointer接口绑定缓冲区与attribute顶点属性
  • 使用gl.enableVertexAttribArray接口开启顶点属性数组,如果不开启的话,默认会使用静态属性,就是所有顶点数据都使用一个默认的值
    代码如下:
gl.vertexAttribPointer(a_Position, 4, gl.FLOAT, false, FSIZE * 4, 0);
gl.enableVertexAttribArray(a_Position);

绘制

当前程序使用gl.drawArrays来开始绘制操作
gl.drawArrays的第一个参数是绘制的类型,我们当前程序就使用gl.POINTS方式
gl.drawArrays的第二个参数是绘制顶点的偏移,当前为0
gl.drawArrays的第三个参数是绘制顶点的数量,当前为4
代码如下:

gl.clearColor(0.0,0.0,0.0,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 4);

代码结构调整

  • 我们把创建WebGL上下文和初始化着色器的方法放进sk_utils.js
  • 我们把shader字符串放进sk_shader.js
  • 我们把传递数据的部分放进sk_init.js
  • main.html中需要添加对这3个js的引用,我们已经添加好了

完整的代码已经上传gitlab:
https://gitlab.com/lingyanTools/sk_webgl.git
该例子绘制了四个顶点
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

聊聊百度造车

10月27日&#xff0c;极越-01上市&#xff0c;一个月后大幅降价&#xff0c;时至今日距离发布已经过去了两个月&#xff0c;官方迟迟不肯公布销量&#xff0c;实际情况大家也都心知肚明。 如今小米汽车技术发布会风头无两&#xff0c;而同一年宣布造车的极越却无人问津&#x…

算法随想录第四十八天打卡| 198.打家劫舍 , 213.打家劫舍II , 337.打家劫舍III

详细布置 今天就是打家劫舍的一天&#xff0c;这个系列不算难&#xff0c;大家可以一口气拿下。 198.打家劫舍 视频讲解&#xff1a;动态规划&#xff0c;偷不偷这个房间呢&#xff1f;| LeetCode&#xff1a;198.打家劫舍_哔哩哔哩_bilibili 代码随想录 class Solution(…

【智能家居入门3】(MQTT服务器、MQTT协议、微信小程序、STM32)

前面已经写了三篇博客关于智能家居的&#xff0c;服务器全都是使用ONENET中国移动&#xff0c;他最大的优点就是作为数据收发的中转站是免费的。本篇使用专门适配MQTT协议的MQTT服务器&#xff0c;有公用的&#xff0c;也可以自己搭建&#xff08;应该要钱&#xff09;&#xf…

华为配置ARP安全综合功能实验

配置ARP安全综合功能示例 组网图形 图1 配置ARP安全功能组网图 ARP安全简介配置注意事项组网需求配置思路操作步骤配置文件 ARP安全简介 ARP&#xff08;Address Resolution Protocol&#xff09;安全是针对ARP攻击的一种安全特性&#xff0c;它通过一系列对ARP表项学习和A…

Qt应用开发(安卓篇)——调用ioctl、socket等C函数

一、前言 在 Qt for Android 中没办法像在嵌入式linux中一样直接使用 ioctl 等底层函数&#xff0c;这是因为因为 Android 平台的安全性和权限限制。 在 Android 中&#xff0c;访问设备硬件和系统资源需要特定的权限&#xff0c;并且需要通过 Android 系统提供的 API 来进行。…

在线版XD,免费使用,功能全面,设计神器!

Adobe XD是什么软件&#xff1f; Adobe XD软件是一个结合设计和建立原型功能的一站式UX/UI设计平台。在XD软件中&#xff0c;数字团队可以进行移动应用、网页设计和原型制作。与此同时&#xff0c;Adobe XD软件也是一种跨平台设计产品&#xff0c;结合设计和建立原型功能&…

力扣 122.买卖股票的最佳时机 II

代码&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {if(prices.size()1) return 0;int res 0;int i0;while(i<prices.size()-1){int ji1;if(prices[j]>prices[i]){//在找到对应元素的下一个元素比他大的时候买入while(j1 < p…

研学活动报名平台源码开发方案

一、项目背景与目标 &#xff08;一&#xff09;项目背景 研学活动报名平台旨在为活动组织者提供方便快捷的研学活动管理工具&#xff0c;同时为用户提供全面的活动搜索、报名和支付等功能。通过该系统&#xff0c;活动组织者能够更好地管理活动报名信息&#xff0c;用户也可…

系统架构设计师-22年-下午题目

系统架构设计师-22年-下午题目 更多软考知识请访问 https://ruankao.blog.csdn.net/ 试题一必答&#xff0c;二、三、四、五题中任选两题作答 试题一 (25分) 说明 某电子商务公司拟升级其会员与促销管理系统&#xff0c;向用户提供个性化服务&#xff0c;提高用户的粘性。…

Unity之第一人称角色控制

目录 第一人称角色控制 &#x1f634;1、准备工作 &#x1f4fa;2、鼠标控制摄像机视角 &#x1f3ae;3、角色控制 &#x1f603;4.杂谈 第一人称角色控制 专栏Unity之动画和角色控制-CSDN博客的这一篇也有讲到角色控制器&#xff0c;是第三人称视角的&#xff0c;以小编…

【LeetCode】142. 环形链表 II

leetcode题目链接 142. 环形链表 II /*** Definition for singly-linked list.* struct ListNode {* int val;* struct ListNode *next;* };*/ typedef struct ListNode ListNode;ListNode *detectCycle(ListNode *head) {ListNode *slow head, *fast head;while (…

【读点论文】SPTS Single-Point Text Spotting

SPTS Single-Point Text Spotting ABSTRACT 现有的场景文本识别(即&#xff0c;端到端文本检测和识别)方法依赖于昂贵的边界框注释(例如&#xff0c;文本行&#xff0c;词级或字符级边界框)。我们首次证明&#xff0c;训练场景文本识别模型可以通过对每个实例的单点进行极低成…

Cmake编译Opencv3.3.1遇到有些文件无法下载的错误解决:

前言&#xff1a; 对于&#xff0c;opencv有些配置文件错误并未致命&#xff0c;所以&#xff0c;有错误也不影响后续的编译&#xff1a;但是&#xff0c;后引用如果要用&#xff0c;在回过头来还是要解决的。 问题表述&#xff1a; 比如&#xff0c;有些文件下载的错误&am…

uni-app在hbuilderx打开微信开发工具运行

一、运行设置配置微信开发者工具路径 运行-运行到小程序模拟器-运行设置 配置微信开发工具的安装路径&#xff08;可浏览文件位置选择&#xff09;&#xff1b;web服务器端口号在第二步骤获得&#xff1b; 二、打开微信开发者工具设置-安全设置 打开服务端口开关&#xff0…

qt中使用mysql 数据库

QT 版本介绍 虽然版本是这个&#xff0c;但是工作目录确是&#xff1a; 下面陈述安装步骤 第一步&#xff1a; 就是安装MYSQL 数据库&#xff0c;在此不再赘述了&#xff0c;很多博主已经上传了。 第二步&#xff1a; 就是拷贝QT 对应mysql 的版本驱动到 QT 的编译器文件中…

华为VRP系统简介

因为现在国内主流是华为、华三、锐捷的设备趋势&#xff0c;然后考的证书也是相关的&#xff0c;对于华为设备的一个了解也是需要的。 一、VRP概述 华为的VRP(通用路由平台)是华为公司数据通信产品的通用操作系统平台&#xff0c;作为华为公司从低端到核心的全系列路由器、以太…

vue核心知识点

一、Vue基础知识点总结 开发vue项目的模式有两种&#xff1a; 基于vue.js&#xff0c;在html中引入vue.js&#xff0c;让vue.js管理div#app元素。基于脚手架环境&#xff1a;通过vue脚手架环境可以方便的创建一个通用的vue项目框架的模板&#xff0c;在此基础之上开发vue项目…

Flutter 点击空白处关闭软键盘,点击非TextField 关闭软键盘的方法

1&#xff1a;点击空白处(非控件上)关闭软键盘。 此方法有个问题&#xff0c;就是点击非空白区域&#xff0c;不会关闭软键盘&#xff0c;比如点击旁边的其他按钮&#xff0c;则软键盘还在。只适合点击空白处关闭软键盘 在 main.dart 入口 build 中增加 builder: (context, ch…

计算机网络_1.4 计算机网络的定义和分类

1.4 计算机网络的定义和分类 一、计算机网络的定义&#xff08;无唯一定义&#xff09;二、计算机网络的分类&#xff08;从不同角度分类&#xff09;1、交换方式2、使用者3、传输介质4、覆盖范围5、拓扑结构 笔记来源&#xff1a; B站 《深入浅出计算机网络》课程 一、计算机…

Springboot整合Websocket实现ws和wss连接

1. 引入pom依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId><version>2.7.10</version> </dependency>2. 新建websocket配置文件 import org.springf…