这是一篇研究计算机图形学的文章。而webGL(几乎没用他的特性)只是一种工具。用canvas2D也能实现相同的功能。
本文记录了研究的体会,难免有错。欢迎有识之士一起讨论研究,不吝赐教。
先上效果图:
(一)向量篇
向量是计算机图形学中最基本的元素。一般由三个坐标组成vector(x, y, z)。他的基本运算有:加法(add),减法(subtract),数乘(multiply),数除(divide),点乘(dot),叉乘(cross),归一化(normalize),length(模运算)。
下面给出vector运算的javascript代码
(function() {
Vector = function(x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
Vector.prototype = {
add: function(v) {
return Vector.create(this.x + v.x, this.y + v.y, this.z + v.z);
},
substract: function(v) {
return Vector.create(this.x - v.x, this.y - v.y, this.z - v.z);
},
multiply: function(n) {
return Vector.create(this.x * n, this.y * n, this.z * n);
},
divide: function(n) {
return Vector.create(this.x / n, this.y / n, this.z / n);
},
dot: function(v) {
return this.x * v.x + this.y * v.y + this.z * v.z;
},
cross: function(v) {
return Vector.create(
this.y * v.z - this.z * v.y,
this.z * v.x - this.x * v.z,
this.x * v.y - this.y * v.x
);
},
length: function() {
return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z);
},
normalize: function() {
var len = this.length();
return Vector.create(
this.x / len,
this.y / len,
this.z / len
);
},
toString: function() {
return "x:" + this.x + "\ny:" + this.y + "\nz:" + this.z;
}
};
Vector.create = function(x, y, z) {
return new Vector(x, y, z);
};
})();
(二)几何篇
1. 球和线的交点
要渲染图形,最简单的莫过于球了。
球在数学中的定义:
|| Surface - Center || = radius
而我们看到的球其实是以我们眼睛为起点产生的射线组成的视椎体和球表面的交点集合。
直线方程在解析几何中如下定义:
F(x) = origin + distance * direction (1)
其中origin是直线经过的点,distance是距离,是个数字,direction是单位向量,仅仅表示距离
上面的直线方程可以理解为经过origin,方向为direction的点集。
球方程两边平方,得
pow(surface - center, 2) = radius * radius (2)
经(1)(2)式联立:
pow(origin -center + distance * direction, 2) = radius * radius
设v = origin-center
pow(v, 2) + 2 * distance * direction * v + pow(distance * direction, 2) = pow(radius, 2)
direction是单位向量,平方=1
上式等于
pow(v, 2) + 2 * distance * direction * v + pow(direction, 2) = pow(radius, 2)
经二次曲线万能求根公式
a = 1
b = 2 * distance * direction * v
c = pow(v, 2) - pow(radius, 2)
d = pow(b, 2) - 4 * a * c
这里有几个限制。
1. dot(direction, v)必须小于0。因为方向是不一样的。如果>=0代表摄像机反向的镜像。
2. d >=0
3. 由于求的是距离,所以distance必须要>0,否则就是direction有问题。
4. 由于求的是最短距离,所以只需要求一个根
最后的求距离的式子整理如下:
distance = - direction * v - sqrt(pow(direction * v, 2) - pow(v, 2) + pow(radius, 2))
相关实现的GLSL代码:
bool intersectSphere(vec3 center, vec3 lStart, vec3 lDir, out float dist) {
vec3 c = lStart - center;
float b = dot(c, lDir);
/**
* 当球心和CameraPos的向量和Ray向量(方向相反)成钝角的时候,才显示到屏幕上
*/
if (b > 0.0) {
dist = 10000.0;
return false;
}
/* radius:0.1 */
float d = b * b - dot(c, c) + 0.01;
if (d < 0.0) {
dist = 10000.0;
return false;
}
// 求最短距离,所以sqrt前的符号为-
dist = -b - sqrt(d);
if (dist < 0.0) {
dist = 10000.0;
return false;
}
return true;
}
2.平面和线的交点
定义平面上的交点为p(x, y, z)
平面上的任意一点为p1(x1, y1, z1)
平面的法线为VP
直线方程为F(P) = origin + d * direction
对于平面上任意向量(p - p1)有 (p - p1) * VP = 0
将p用直线方程联立有
(origin - p1 + d * direction) * VP = 0
(origin - p1) * VP + d * direction * VP = 0
(p1 - origin) * VP / direction * VP = d
如果d=0,代表没有交点(平行)
如果d<0,说明direction有问题。
判断平面的代码如下:
bool intersectPlane(vec3 lStart, vec3 lDir, out float dist) {
vec3 normal = normalize(vec3(0.0, 1.0, 0.0));
float a = dot(lDir, normal);
if (a > 0.0) {
dist = 10000.0;
return false;
}
float b = dot(normal, lStart - vec3(0.0, 0.0, 0.0));
dist = -b / a;
return true;
}
(三)场景篇
大家拿着照相机随拍的时候,是不是远处的东西小,近处的东西大?数码照相机屏幕上的画面就是远处那个物体在照相机镜头上的投影。也就是说,从镜头开始,展开了一个视锥体。视锥体的横截面就是照相机的屏幕。要模拟创建这个视锥体。首先定义相机镜头的起始位置
var cameraFrom = Vector.create(0, 0.4, 1);
相机镜头的目标位置
var cameraTo = Vector.create(0, 0, 0);
得到光线向量:cameraDirection = cameraFrom.substract(cameraTo);
利用光线向量创建视椎体
首先利用相机方向和y轴无限向量创建视椎体左边的向量
cameraLeft = cameraDirection.cross(vec3(0, 1, 0)).normalize();
再利用cameraDirection和cameraLeft的叉乘求出视椎体向上的向量,并乘以高宽比
cameraTop = cameraDirection.cross(camerLeft).normalize().multiply(canvas's height / canvas's width);
那么左上角的向量cameraLeftTop = cameraDirection.substract(cameraLeft).add(cameraTop).normalize();
右上角cameraRightTop = cameraDirection.add(cameraLeft).add(cameraTop).normalize();
左下角cameraLeftBottom = cameraDirection.substract(cameraLeft).
substract(cameraTop).normalize();
右下角cameraRightBottom = cameraDirection.add(cameraLeft). substract(cameraTop).normalize();
最后补充下webGL的gl.TRIANGLE_STRIP的绘画顺序。按照左上,右上,左下,右下的顺序画出一个四方形(两三角形)
然后,由于webGL的Y轴是和浏览器相反的。
本来绘画的顺序是左上,右上,左下,右下。现在经沿X轴翻转后,顺序变为右上,左上,右下,左下
实例代码如下:
// radio = height / width
var radio = canvas.height / canvas.width;
var cameraPersp = 2;
var cameraDir = cameraTo.substract(cameraFrom).normalize();
// camera line
var cameraCenter = cameraFrom.add(cameraDir.multiply(cameraPersp));
var cameraLeft = cameraDir.cross(up).normalize();
var cameraTop = cameraLeft.cross(cameraDir).normalize().multiply(radio);
var cameraLeftTop = cameraCenter.add(cameraLeft).add(cameraTop).normalize();
var cameraRightTop = cameraCenter.substract(cameraLeft).add(cameraTop).normalize();
var cameraLeftBottom = cameraCenter.add(cameraLeft).substract(cameraTop).normalize();
var cameraRightBottom = cameraCenter.substract(cameraLeft).substract(cameraTop).normalize();
var corner = [];
// Z字按照X轴翻转后的顺序,因为渲染时候的Y和浏览器的Y刚好相反
pushVector(cameraRightTop, corner);
pushVector(cameraLeftTop, corner);
pushVector(cameraRightBottom, corner);
pushVector(cameraLeftBottom, corner);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(corner), gl.STATIC_DRAW);
gl.uniform3f(cameraPos, cameraFrom.x, cameraFrom.y, cameraFrom.z);
gl.uniform3f(sphere1Center, x1, y1, z1);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
requestAnimFrame(drawScene);
(四)材质篇
这里涉及到冯氏光照模型的原理:
大致意思为光照=环境光+漫反射+镜面反射。
这部分内容在以后的文章中探讨,敬请期待。
实例的GLSL代码:
// *************
// diffuse light
// *************
float diffuseWighting = max(dot(normalize(lightDir), normal), 0.0);
// *************
// specular light
// *************
vec3 eyeDirection = normalize(-vPosition.xyz);
vec3 reflectionDirection = reflect(-lightDir, normal);
// factor: 32
float specularLightWeighting = pow(max(dot(reflectionDirection, eyeDirection), 0.0), 32.0);
color = ambientColor + diffuseWighting * diffuseColor + specularLightWeighting * specularColor;
(五)关于示例代码启动
win7下任何支持webGL的浏览器都可以直接运行本示例。
XP下请http://www.hiwebgl.com/,点击右边的大按钮,<<我为什么无法运行webGL>>。或者直接点击http://www.hiwebgl.com/?p=628#WebGL-2
分享到:
相关推荐
标题《Volume Ray Casting in WebGL》指出了文章的主题是关于在WebGL中实现体积体绘制技术。WebGL(Web图形库)是一种JavaScript API,用于在不需要插件的情况下在网页上渲染2D和3D图形。体绘制(Volume Rendering)...
WebGL是一种基于OpenGL标准的JavaScript API,用于在任何兼容的Web浏览器中实现交互式的2D和3D图形渲染。这个“WEBGL_webgl_”很可能是一份教程或教材,旨在帮助初学者掌握WebGL技术,并随着学习者的进步不断更新...
Unity通用WebGL模板Universal WebGL Template 1.2.1是一个专为开发WebGL游戏或应用程序设计的工具。Unity是一款强大的跨平台游戏引擎,它允许开发者创建高质量的3D和2D游戏,并将其发布到多种设备上,包括浏览器。...
《WebGL编程指南》的主要篇幅讲解了WebGL 原生API 和三维图形学的基础知识,包括渲染管线、着色器、矩阵变换、着色器编程语言(GLSL ES)等等,也讲解了使用WebGL 渲染三维场景的一般技巧,如光照、阴影、雾化等等。...
为了实现真实的反射和折射效果,项目可能还使用了纹理映射技术。纹理可以看作是附在几何表面的图像,通过调整纹理坐标和应用不同的纹理过滤模式,可以模拟出水面的光泽和波动的视觉效果。同时,深度测试和光照模型的...
通过调整光照参数,可以实现不同的阴影和反射效果。 最后,WebGL教程通常会涉及性能优化技巧,如批处理渲染和最小化状态更改,以减少GPU的工作量并提高网页应用的性能。 总的来说,WebGL教程是学习如何在Web平台上...
《2D WebView for WebGL Web Browser 4.0:在Web端构建沉浸式互动体验》 在当前数字化的时代,Web浏览器已经成为了人们获取信息、娱乐和交互...对于希望在Web领域创新的开发者来说,这无疑是一个值得深入研究的技术。
WebGL是一种基于OpenGL标准的JavaScript API,用于在任何兼容的Web浏览器中进行2D和3D图形渲染。它使得开发者可以在无需插件的情况下,在网页上实现高性能的图形处理,为互联网用户提供了丰富的交互式视觉体验。 一...
通过"程序示例-明暗处理",我们可以学习到如何在WebGL中实现基本的明暗处理,比如Phong光照模型,它结合了环境光、漫反射和镜面反射来创建丰富的表面质感。 总的来说,这个“webgl编程小例子”将引导你逐步探索...
WebGL是一种基于OpenGL标准的JavaScript API,用于在任何兼容的Web浏览器中进行2D和3D图形渲染,无需插件。"webgl-debug.js"是一个专门针对WebGL编程的调试库,它通常用于帮助开发者更好地理解和解决问题,提高WebGL...
WebGL是一种基于OpenGL标准的JavaScript API,用于在任何兼容的Web浏览器中实现硬件加速的2D和3D图形渲染。这个“WebGL编程指南函数库”包含了一系列辅助脚本,帮助开发者更方便地进行WebGL编程。以下是这些脚本的...
这个"webgl文档以及工具"的压缩包很可能包含了关于WebGL的学习资料、API参考以及可能的辅助开发工具。 1. **WebGL基础概念**:WebGL是一种JavaScript API,它允许在浏览器中直接进行硬件加速的3D图形编程。通过...
WebGL(Web Graphics Library)是一种JavaScript API,用于在任何兼容的Web浏览器中渲染交互式的2D和3D图形,无需插件。它基于OpenGL ES 2.0标准,并且是HTML5的一个组成部分,使得开发者可以在网页上直接进行图形...
通过深入研究这些源码,你可以逐步理解WebGL的工作原理,学会如何创建复杂的3D场景,并能够独立地解决WebGL编程中遇到的问题。这是一个从理论到实践,从基础知识到高级技术全面学习WebGL的好机会。
Unity WebGL 是一种强大的工具,允许开发者将他们的3D游戏或应用程序直接嵌入到网页中,无需任何插件或额外下载。"Better-Minimal-WebGL-Template" 是一个优化过的Unity WebGL打包模板,旨在提供更好的性能和兼容性...
untiy webgl 打开 PDF文件
【tolua WebGL库】是一个将Lua脚本语言与WebGL图形渲染技术相结合的工具,它允许开发者使用Lua编写游戏或交互式应用的逻辑,并通过WebGL在浏览器中实现高性能的3D图形渲染。这个库的出现使得开发人员可以利用Lua的...