注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中假设有我的额外说明。我会加上[lufy:]。另外,鄙人webgl研究还不够深入,一些专业词语。假设翻译有误,欢迎大家指正。

本次的demo的执行结果

点光源

上次介绍了高氏着色和补色着色。

使用补色着色的手法。能够渲染更加自然的阴影。3D效果更加真实。可是会有计算量比較大的缺点。

这个仅仅能case by case,依据不同的情况来处理了。是个挺烦人的地方。

那么,这次,还是讲光源。我貌似听到了“不会吧......”此类的声音了......
这次的话题是点光源的封装。点光源就像它的名字一样,和顶点一样。光源就是一个点。
到眼下为止。全部的光源处理都是使用了平行光源,平行光源能够看作是从无限远的地方发出的方向固定的光的光源。三维空间中的全部的模型都受到相同方向的光的照耀。而点光源的处理。光源的位置在三维空间中是固定的,三维空间中的模型依据它所在的位置,受到不同方向的光照。
现实世界中相似电光源的有灯泡等。

可是,实际上灯泡的光是会变弱的。距离越远光的强度就越弱。

而这次封装的电光源的处理并没有考虑光的减弱。不管对象距离光源有多元,都受到相同的强度的光的影响,所以。并非全然模拟现实世界中的电光源。

电光源的考虑方法

电光源的封装事实上也并不难。
平行光源的时候是光向量,就是说光的方向是固定的。点光源的时候,光源的位置是确定的,须要算出从光源到顶点的向量作为光向量,用这个光向量来计算阴影。
由于必须计算出光源到顶点的向量,所以比平行光源的计算量要大,可是攻克了光向量的计算之后。就能够沿用之前平行光源的计算。所以也不会太难。

顶点着色器的改动

这次和上次一样。使用补色着色来进行着色,尽管大部分改动都是在片段着色器中进行的,可是顶点着色器中也有少许改动。
点光源的处理,就想刚才说的那样。须要计算光源到顶点的向量,而要计算光向量的话,那就必定须要顶点的位置情报了。
要将顶点着色器中的位置情报传给片段着色器。那肯定须要新的varying变量了。可是仅仅传顶点的位置情报的话,另一些问题。
顶点的位置情报通常以局部坐标的形式传给顶点着色器,所以假设用模型坐标变换对模型进行了移动或者旋转,顶点的位置就会发生变化了。就是说。即使局域坐标是(1.0, 1.0, 1.0)的顶点经过移动。旋转等变换,坐标可能会变成(例 0.5, 2.0, 5.5 )等等。
从点光源发出的光的光向量。必须要考虑模型坐标变换后的顶点的位置。所以,必须向顶点着色器中传入新的模型坐标变换矩阵。那么来改动一下顶点着色器的代码吧。
>顶点着色器代码

attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 mvpMatrix;
uniform mat4 mMatrix;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec4 vColor; void main(void){
vPosition = (mMatrix * vec4(position, 1.0)).xyz;
vNormal = normal;
vColor = color;
gl_Position = mvpMatrix * vec4(position, 1.0);
}

跟上次比較,变更点有两个。

第一个变更点是为了向片段着色器传入顶点的位置情报。追加了varying变量vPosition。由于表示的顶点的位置情报。所以定义了vec3类型。
第二个变更点是追加了新的uniform变量mMatrix。像刚才写的那样,由于顶点着色器中的顶点的位置坐标是局域坐标系,所以为了将模型坐标变换矩阵变换成适当的形式(就是世界坐标系)。使用uniform修饰符定义的变量。以便在着色器一側接受到模型坐标变换矩阵。

向片段着色器中传递顶点的位置情报的时候,表示模型坐标变换矩阵的mMatrix和表示顶点的局部坐标的position相乘,结果带入到vPosition中。这种话。片段着色器就能使用模型坐标变换后的顶点位置了。

片段着色器的改动

接着是片段着色器一側的改动,片段着色器中须要使用顶点的位置和点光源的位置算出光向量。

这时的光向量的计算方法是非常easy的。仅仅须要单纯的减法就能够了。
另外,这次是依据电光源做处理的,所以用表示电光源的位置的uniform变量lightPosition来取代表示光向量的uniform变量lightDirection。
>片段着色器代码

precision mediump float;

uniform mat4 invMatrix;
uniform vec3 lightPosition;
uniform vec3 eyeDirection;
uniform vec4 ambientColor;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec4 vColor; void main(void){
vec3 lightVec = lightPosition - vPosition;
vec3 invLight = normalize(invMatrix * vec4(lightVec, 0.0)).xyz;
vec3 invEye = normalize(invMatrix * vec4(eyeDirection, 0.0)).xyz;
vec3 halfLE = normalize(invLight + invEye);
float diffuse = clamp(dot(vNormal, invLight), 0.0, 1.0) + 0.2;
float specular = pow(clamp(dot(vNormal, halfLE), 0.0, 1.0), 50.0);
vec4 destColor = vColor * vec4(vec3(diffuse), 1.0) + vec4(vec3(specular), 1.0) + ambientColor;
gl_FragColor = destColor;
}

着色器中的main函数的第一行。将从点光源到顶点的光向量代入到变量lightVec中。

像上面说的一样。使用了单纯的减法。非常easy吧。并且,使用这里得到的光向量,和之前平行光源一样求逆矩阵以及半向量。计算扩散光和反射光。
理解了结构,也就应该明确。事实上和之前的demo也没有多大的变化。

主要是光向量的处理不同,光照的方法基本上大致相同。

javascript 的修正

着色器改动完之后。以下就是主程序的javascript的改动了。

这次细节部分改动的比較多,一点点的開始讲解。

眼下为止的demo都仅仅渲染了一个圆环体,这次在圆环体之外加一个球体,看文章一開始的图片就知道了。圆环体的顶点数据以及球体的顶点数据要另外准备。

球体模型的顶点数据的生成,使用以下的函数。和生成圆环体的顶点数据的函数是比較相似的。
>生成球体的顶点数据的函数

// 球体を生成する関数
function sphere(row, column, rad, color){
var pos = new Array(), nor = new Array(),
col = new Array(), idx = new Array();
for(var i = 0; i <= row; i++){
var r = Math.PI / row * i;
var ry = Math.cos(r);
var rr = Math.sin(r);
for(var ii = 0; ii <= column; ii++){
var tr = Math.PI * 2 / column * ii;
var tx = rr * rad * Math.cos(tr);
var ty = ry * rad;
var tz = rr * rad * Math.sin(tr);
var rx = rr * Math.cos(tr);
var rz = rr * Math.sin(tr);
if(color){
var tc = color;
}else{
tc = hsva(360 / row * i, 1, 1, 1);
}
pos.push(tx, ty, tz);
nor.push(rx, ry, rz);
col.push(tc[0], tc[1], tc[2], tc[3]);
}
}
r = 0;
for(i = 0; i < row; i++){
for(ii = 0; ii < column; ii++){
r = (column + 1) * i + ii;
idx.push(r, r + 1, r + column + 2);
idx.push(r, r + column + 2, r + column + 1);
}
}
return {p : pos, n : nor, c : col, i : idx};
}

形成球体的顶点,定义了一个用一个大的多边形群组成的膜裹成球的形状方法。这个sphere函数接受四个參数。第一个參数是形成球体的膜状的多边形板的纵向切割数(顶点数)。用地球比喻的话就是纬度的方向。

第二个參数则是横向切割数,这里用地球来说的话,就是经度的方向。

第三个參数是球体的半径。第四个參数是球体的颜色。这个颜色是包括四个元素的数组,假设没有指定颜色。则会自己主动分配HSV颜色。

这个函数的用法,传入适当的參数,然后接收返回值。

返回值是一个对象。使用的时候就參照这个对象的适当的属性。实际的代码例如以下。

>函数sphere的使用部分

// 用球体的顶点数据生成VBO并保存
var sphereData = sphere(64, 64, 2.0, [0.25, 0.25, 0.75, 1.0]);
var sPosition = create_vbo(sphereData.p);
var sNormal = create_vbo(sphereData.n);
var sColor = create_vbo(sphereData.c);
var sVBOList = [sPosition, sNormal, sColor]; // 球体用IBO的生成
var sIndex = create_ibo(sphereData.i);

上面的代码,生成一个纵向和横向都是64个顶点的球体,半径是2.0,这次指定的颜色是蓝色。须要注意的是,为了后面的处理,将VBO保存到了数组中,这样做了之后。attributeLocation和VBO联系的工作就变的非常的方便了,这个后面会叙述。
接着是uniformLocation的获取部分,这次是从平行光源变到电光源,指定光的方向的部分要换成指定光的位置。

>uniform的相关处理

// uniformLocationを配列に取得
var uniLocation = new Array();
uniLocation[0] = gl.getUniformLocation(prg, 'mvpMatrix');
uniLocation[1] = gl.getUniformLocation(prg, 'mMatrix');
uniLocation[2] = gl.getUniformLocation(prg, 'invMatrix');
uniLocation[3] = gl.getUniformLocation(prg, 'lightPosition');
uniLocation[4] = gl.getUniformLocation(prg, 'eyeDirection');
uniLocation[5] = gl.getUniformLocation(prg, 'ambientColor'); // 中略 // 点光源的位置
var lightPosition = [0.0, 0.0, 0.0];

着色器中进行的uniform修饰符变量的变更在这里都具体的反映出了,并且,demo的电光源的位置设定成了原点。
为了更easy明确点光源的效果,demo中的以点光源的位置为中心,圆环体和球体不断的进行旋转,里面包括了模型坐标变换矩阵的生成。

由于同一时候绘制两个模型,在持续循环的过程中,使用适当的VBO和IBO进行模型的渲染。

代码略微长了点。细致看的话就明确了。主要是刚才写的那样,使用保存有VBO的数组,在自制的函数中对VBO进行绑定处理。

>持续循环的绘制处理

// カウンタをインクリメントする
count++; // カウンタを元にラジアンと各種座標を算出
var rad = (count % 360) * Math.PI / 180;
var tx = Math.cos(rad) * 3.5;
var ty = Math.sin(rad) * 3.5;
var tz = Math.sin(rad) * 3.5; // トーラスのVBOとIBOをセット
set_attribute(tVBOList, attLocation, attStride);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, tIndex); // モデル座標変換行列の生成
m.identity(mMatrix);
m.translate(mMatrix, [tx, -ty, -tz], mMatrix);
m.rotate(mMatrix, -rad, [0, 1, 1], mMatrix);
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
m.inverse(mMatrix, invMatrix); // uniform変数の登録と描画
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, mMatrix);
gl.uniformMatrix4fv(uniLocation[2], false, invMatrix);
gl.uniform3fv(uniLocation[3], lightPosition);
gl.uniform3fv(uniLocation[4], eyeDirection);
gl.uniform4fv(uniLocation[5], ambientColor);
gl.drawElements(gl.TRIANGLES, torusData.i.length, gl.UNSIGNED_SHORT, 0); // 球体のVBOとIBOをセット
set_attribute(sVBOList, attLocation, attStride);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, sIndex); // モデル座標変換行列の生成
m.identity(mMatrix);
m.translate(mMatrix, [-tx, ty, tz], mMatrix);
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
m.inverse(mMatrix, invMatrix); // uniform変数の登録と描画
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, mMatrix);
gl.uniformMatrix4fv(uniLocation[2], false, invMatrix);
gl.drawElements(gl.TRIANGLES, sphereData.i.length, gl.UNSIGNED_SHORT, 0); // コンテキストの再描画
gl.flush();

各种坐标变换矩阵的生成,以及逆矩阵的生成结束后,将点光源的位置和视点向量等一起传入着色器,以及VBO和IBO的绑定处理之后,发出画图命令。

为了绘制两个模型而进行了一连串的处理,可是要注意不要反复。也没进行特别难的处理。

总结

用点光源的光照,概念基本上和平行光源一样。依据获取光向量和顶点的法线及视点向量的内积来加入阴影。和平行光源的不同之处,简单的说就是光向量是否是一个固定值。点光源使用的是模型坐标变换后的顶点的位置和光源的位置,这时再计算光向量,所以添加了若干的计算量。

平行光源的光的方向是一定的。总体都受到均等的光照。可是点光源依据实际顶点的坐标要进行具体的光的碰撞。这次的demo和上次一样在片段着色器中进行光的计算和补色着色,所以能够进行非常美丽的渲染。

这次的文章中仅仅须要明确是进行了光照相关的基础部分的封装,WebGL中的着色依据功夫是否到家,能够渲染出各种效果,从如今開始要有应用的能力了,以后会进行一些特殊的技术的具体的介绍。

那么,这次也提供了实际的demo,请点击连接进行測试。

下次,要開始图片的渲染了,期待吧。

用点光源来渲染圆环体和球体

http://wgld.org/s/sample_013/

转载请注明:转自lufy_legend的博客http://blog.csdn.net/lufy_legend

[WebGL入门]二十五,点光源的光照的更多相关文章

  1. [WebGL入门]二十四,补色着色

    注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中假设有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,假设翻译有误,欢迎大家指 ...

  2. Bootstrap入门(二十五)JS插件2:过渡效果

    Bootstrap入门(二十五)JS插件2:过渡效果 对于简单的过渡效果,只需将 transition.js 和其它 JS 文件一起引入即可.如果你使用的是编译(或压缩)版的bootstrap.js  ...

  3. FreeSql (二十五)延时加载

    FreeSql 支持导航属性延时加载,即当我们需要用到的时候才进行加载(读取),支持1对1.多对1.1对多.多对多关系的导航属性. 当我们希望浏览某条订单信息的时候,才显示其对应的订单详细记录时,我们 ...

  4. Bootstrap <基础二十五>警告(Alerts)

    警告(Alerts)以及 Bootstrap 所提供的用于警告的 class.警告(Alerts)向用户提供了一种定义消息样式的方式.它们为典型的用户操作提供了上下文信息反馈. 您可以为警告框添加一个 ...

  5. 无废话ExtJs 入门教程十五[员工信息表Demo:AddUser]

    无废话ExtJs 入门教程十五[员工信息表Demo:AddUser] extjs技术交流,欢迎加群(201926085) 前面我们共介绍过10种表单组件,这些组件是我们在开发过程中最经常用到的,所以一 ...

  6. VMware vSphere 服务器虚拟化之二十五 桌面虚拟化之终端服务池

    VMware vSphere 服务器虚拟化之二十五 桌面虚拟化之终端服务池 终端服务池是指由一台或多台微软终端服务器提供服务的桌面源组成的池.终端服务器桌面源可交付多个桌面.它具有以下特征: 1.终端 ...

  7. WCF技术剖析之二十五: 元数据(Metadata)架构体系全景展现[元数据描述篇]

    原文:WCF技术剖析之二十五: 元数据(Metadata)架构体系全景展现[元数据描述篇] 在[WS标准篇]中我花了很大的篇幅介绍了WS-MEX以及与它相关的WS规范:WS-Policy.WS-Tra ...

  8. Bootstrap入门(十五)组件9:面板组件

    Bootstrap入门(十五)组件9:面板组件 虽然不总是必须,但是某些时候你可能需要将某些 DOM 内容放到一个盒子里.对于这种情况,可以试试面板组件. 1.基本实例 2.带标题的面板 3.情景效果 ...

  9. JAVA基础再回首(二十五)——Lock锁的使用、死锁问题、多线程生产者和消费者、线程池、匿名内部类使用多线程、定时器、面试题

    JAVA基础再回首(二十五)--Lock锁的使用.死锁问题.多线程生产者和消费者.线程池.匿名内部类使用多线程.定时器.面试题 版权声明:转载必须注明本文转自程序猿杜鹏程的博客:http://blog ...

随机推荐

  1. 浅谈JavaScript中的null和undefined

    浅谈JavaScript中的null和undefined null null是JavaScript中的关键字,表示一个特殊值,常用来描述"空值". 对null进行typeof类型运 ...

  2. POJ 2109 Inner Vertices(扫描线+树状数组)

    [题目链接] http://poj.org/problem?id=3109 [题目大意] 在一个棋盘上放满白子,现在把一些白子变成黑子, 如果一个白子上下左右都有黑子,就会变成黑子,问最终黑子个数 [ ...

  3. [给自己扫盲]名词解释——LAMP、MEAN、Web应用框架等

    名词解释 LAMP The LAMP software bundle (here additionally with Squid). A high performance and high-avail ...

  4. 设置并删除Dreamweaver自动生成的_notes文件夹

    在使用Dreamweaver做项目时站点下面的每个文件夹里面都会自动生成一个_notes文件夹,删除之后马上又会再次生成.最近做项目时,有童鞋一不小心把所有的_notes文件夹全部存回到SVN上面了, ...

  5. JAVA常见算法题(十六)

    package com.xiaowu.demo; //猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个: //第二天早上又将剩下的桃子吃掉一半,而且又多吃了一个. //以后 ...

  6. apache 限制某些目录不能访问通过rewrite实现

    通过deny allow访问控制肯定是可以实现的单个目录,但是这个必须指定准确的目录,如果有很多个目录,但是都包含某个名字,比如bbs.1.com/1/tmp/123.htmlbbs.1.com/2/ ...

  7. IP分类:A,B,C,D,E五类

    IP地址分为五类: IP地址分为五类:A类保留给政府机构,B类分配给中等规模的公司,C类分配给任何需要的人,D类用于组播,E类用于实验. 常用的三类IP地址 IP = 网路地址(网络号)+主机地址(主 ...

  8. Makefile之嵌套执行make

    1.在Makefile中 一般使用一个总控Makefile来指明文件的编译规则 如: subsystem: cd subdir && $(MAKE) 其等价于 subsystem: $ ...

  9. C# 窗体位置 Show和ShowDialog (转载)

    CenterParent                     窗体在其父窗体中居中.       CenterScreen                    窗体在当前显示窗口中居中,其尺寸在 ...

  10. php不重新编译添加模块

    php不重新编译添加模块 本文以安装mysqli模块为例 一.检查 1:首先保证php-fpm能正常启动 2:查看当前已安装的php模块是否有mysqli [root@web01 ~]# /appli ...