上一篇已经讲述了通过面模拟线条时候,每一个顶点的顶点数据包括:端点坐标、偏移量、前一个端点坐标、后一个端点坐标,当然如果我们通过索引的方式来绘制的话,还包括索引数组,下面的代码通过传递一组线条的端点数组来创建上述相关数据:
```
bk.Line3D = function (points,colors){
this.points = points;
this.colors = colors;
}
bk.Line3D.prototype.computeData = function() {
var len = this.points.length;
var count = len * 3 * 2;
var position = new Float32Array(count);
var positionPrev = new Float32Array(count);
var positionNext = new Float32Array(count);
var color = new Float32Array(count);
var offset = new Float32Array(len * 2);
var indicesCount = 3 * 2 * (len - 1);
var indices = new Uint16Array(indicesCount);
var triangleOffset = 0,vertexOffset = 0;
for(var i = 0; i < len; i ++){
var i3 = i * 3 * 2;
var point = this.points[i];
position[i3 + 0] = point.x;
position[i3 + 1] = point.y;
position[i3 + 2] = point.z;
position[i3 + 3] = point.x;
position[i3 + 4] = point.y;
position[i3 + 5] = point.z;
var r = (i + 1) / len;
var g = Math.random();
var b = Math.random();
g = r;
b = 0;
r = 1- r;
color[i3 + 0] = r;
color[i3 + 1] = g;
color[i3 + 2] = b;
color[i3 + 3] = r;
color[i3 + 4] = g;
color[i3 + 5] = b;
if (i < count - 1) {
var i3p = i3 + 6;
positionNext[i3p + 0] = point.x;
positionNext[i3p + 1] = point.y;
positionNext[i3p + 2] = point.z;
positionNext[i3p + 3] = point.x;
positionNext[i3p + 4] = point.y;
positionNext[i3p + 5] = point.z;
}
if (i > 0) {
var i3n = i3 - 6;
positionPrev[i3n + 0] = point.x;
positionPrev[i3n + 1] = point.y;
positionPrev[i3n + 2] = point.z;
positionPrev[i3n + 3] = point.x;
positionPrev[i3n + 4] = point.y;
positionPrev[i3n + 5] = point.z;
}
var idx = 3 * i;
var i2 = i * 2;
offset[i2 + 0] = 5;
offset[i2 + 1] = -5;
}
var end = count - 1;
for(i = 0;i < 6 ;i ++){
positionNext[i] = positionNext[i + 6];
positionPrev[end - i] = positionPrev[end - i - 6];
}
for(i = 0;i < indicesCount ;i ++){
if(i % 2 == 0){
indices[triangleOffset ++] = i;
indices[triangleOffset ++] = i + 1;
indices[triangleOffset ++] = i + 2;
}else{
indices[triangleOffset ++] = i + 1;
indices[triangleOffset ++] = i;
indices[triangleOffset ++] = i + 2;
}
}
this.position = position;
this.positionNext = positionNext;
this.positionPrev = positionPrev;
this.color = color;
this.offset = offset;
this.indices = indices;
};
```
代码首先定义了一个类,该类构造函数可以传入端点数组;在该类上定义了一个方法 computeData,用来计算顶点数组,每个顶点包括上文所述的4个信息,另外增加了一个颜色信息。
读者,可以结合第二篇的思路和上面的代码来来理解,此处不再详述 代码的细节。
另外一个比较重要的代码是顶点着色器中,通过传入的这些顶点信息来计算最终的顶点坐标,代码如下:
```
var lineVS = `
attribute vec3 aPosition;
attribute vec3 aPositionPre;
attribute vec3 aPositionNext;
attribute float aOffset;
attribute vec3 aColor;
varying vec3 vColor;
uniform mat4 uWorldViewProjection;
uniform vec4 uViewport;
uniform float uNear;
uniform mat4 uViewMatrix;
uniform mat4 uProjectMatrix;
vec4 clipNear(vec4 p1,vec4 p2){
float n = (p1.w - uNear) / (p1.w - p2.w);
return vec4(mix(p1.xy,p2.xy,n),-uNear,uNear);
}
void main(){
vec4 prevProj = uWorldViewProjection * vec4(aPositionPre, 1.0);
vec4 currProj = uWorldViewProjection * vec4(aPosition, 1.0);
vec4 nextProj = uWorldViewProjection * vec4(aPositionNext, 1.0);
if (currProj.w < 0.0) {
if (prevProj.w < 0.0) {
currProj = clipNear(currProj, nextProj);
}else {
currProj = clipNear(currProj, prevProj);
}
}
vec2 prevScreen = (prevProj.xy / abs(prevProj.w) + 1.0) * 0.5 * uViewport.zw;
vec2 currScreen = (currProj.xy / abs(currProj.w) + 1.0) * 0.5 * uViewport.zw;
vec2 nextScreen = (nextProj.xy / abs(nextProj.w) + 1.0) * 0.5 * uViewport.zw;
vec2 dir;
float len = aOffset;
if(aPosition == aPositionPre){
dir = normalize(nextScreen - currScreen);
}else if(aPosition == aPositionNext){
dir = normalize(currScreen - prevScreen);
}else {
vec2 dirA = normalize(currScreen - prevScreen);
vec2 dirB = normalize(nextScreen - currScreen);
vec2 tanget = normalize(dirA + dirB);
float miter = 1.0 / max(dot(tanget,dirA),0.5);
len *= miter;
dir = tanget;
}
dir = vec2(-dir.y,dir.x) * len;
currScreen += dir;
currProj.xy = (currScreen / uViewport.zw - 0.5) * 2.0 * abs(currProj.w);
vec4 pos = uProjectMatrix * uViewMatrix * vec4(aPosition,1.0);
vColor = aColor;
gl_Position = currProj;
}
`;
```
计算的原理,也可以参考第二篇的论述,此处需要注意的是,为了能够计算顶点在屏幕上的最终位置,需要把canvans的尺寸大小传递给着色器(uniform 变量 uViewport),同样为了计算裁剪,需要把镜头的near值传递给着色器(uniform 变量 uNear),而变量uWorldViewProjection表示模型视图透视变换的矩阵,熟悉WebGL的同学一定清楚。
如果你有兴趣,请关注公众号。
相关推荐
WebGL之绘制三维地球。
看的懂,需要自己具备对js有自己的理解,原型链,this的指向,c++,webgl的基本理解和使用,ip地址是不能看的,因为播放的流量都是收费的,如果你们有ip,可以连接你们自己的ip地址,逻辑就是这样的逻辑,你让我分析...
webgl入门-基础三角形绘制
网页动画素材 WebGL基于canvas画布绘制3D噪音线条酷炫动画特效。(抖音资料)网页动画素材 WebGL基于canvas画布绘制3D噪音线条酷炫动画特效。(抖音资料)网页动画素材 WebGL基于canvas画布绘制3D噪音线条酷炫动画...
:tangerine: image3D使用webGL绘制三维图片。:bar_chart::chart_increasing::party_popper: Drawing three-dimensional images using webGL.鉴于当前浏览器支持情况,本项目只支持webGL 1上下文,更高级版本未来会...
Sierpinski_Triangle WebGL Sierpinski三角形
JavaScript基于WebGL技术实现的三维BS端开发平台(含Cesium 的核心操作库+UI组件库).zipJavaScript基于WebGL技术实现的三维BS端开发平台(含Cesium 的核心操作库+UI组件库).zipJavaScript基于WebGL技术实现的三维BS端...
WebGLVolumeRendering, WebGL体绘制容易 WebGL体绘制容易一个非常简单的步骤介绍像素着色器体渲染使用和 ThreeJS 。转到 http://lebarba.com/blog/ 一步教程中的步骤在 GitHub http://www.lebarba.com/WebGL/
webgl旋转三角形生成。后续webgl基础随笔持续更新。git地址:https://github.com/11zouzouzou
《WebGL编程指南》的主要篇幅讲解了WebGL 原生API 和三维图形学的基础知识,包括渲染管线、着色器、矩阵变换、着色器编程语言(GLSL ES)等等,也讲解了使用WebGL 渲染三维场景的一般技巧,如光照、阴影、雾化等等。...
这是一个用WebGL编写的三维分裂三角形,实现了三角形的自动分裂,可以开始,暂停,终止,代码简单,可以参考,这也是西南石油大学南充校区,计算机图形学第三个实验!
WebGL 3D雪花飘落动画特效是一款适合用作网页背景特效下载。
有兴趣刚需的可以自己下载,非常实用的特效代码,可以完美运行,有能力的还可以二次修改!
基于vue3.0+esmap+springboot+WebGL的智慧楼宇三维监控系统源码(毕设项目).zip 基于vue3.0+esmap+springboot+WebGL的智慧楼宇三维监控系统源码(毕设项目).zip 基于vue3.0+esmap+springboot+WebGL的智慧楼宇三维监控...
webgl入门-基础矩形绘制
这是一个简单的三角形代码,下载下来就能用,这也是西南石油大学南充校区,计算机图形学实验的第一个实验,制作一个三角形!
小游戏webgl与canvas2d混合使用demo,将context2d作为纹理用webgl绘制
Drawing three-dimensional images using webGL
《WebGL编程指南》的主要篇幅讲解了WebGL 原生API 和三维图形学的基础知识,包括渲染管线、着色器、矩阵变换、着色器编程语言(GLSL ES)等等,也讲解了使用WebGL 渲染三维场景的一般技巧,如光照、阴影、雾化等等。...