用線段繪制球體(three.js webgl_lines_spere例子)
Three.js中的webgl_lines_sphere例子使用線段渲染出3D球體滚局,效果十分有趣。
實現(xiàn)過程
渲染過程比較簡單,先構(gòu)造出單位球體上(unit sphere)的點羡洛,隨后將點再對應(yīng)半徑上隨機伸縮坐標柑司,每個半徑上相鄰的這兩個點構(gòu)成線段,為線段指定色彩后即可渲染出基本的lines sphere(線段球體)拷呆。
這里闲坎,我們模仿webgl_lines_sphere例子,使用C++和OpenGL ES 3.0獲得了如下的渲染效果茬斧,iOS版本實現(xiàn)源碼可以從github上獲取腰懂。
用線段繪制一個基本的3D球體
這一過程的關(guān)鍵在于使用線段代替頂點形成球形幾何體數(shù)據(jù)。首先啥供,我們使用隨機數(shù)分別產(chǎn)生范圍為[-1..1]的各個坐標部件的數(shù)值,然后把這些坐標看作矢量悯恍,并進行標準化。隨后伙狐,我們再將這些矢量的坐標看作點的坐標涮毫,那么這些點將位于中心為原點的單位球體之上。如此贷屎,我們就擁有了一個球體的頂點數(shù)據(jù)罢防,之后,再針對這個頂點沿著半徑方向(也是這個點的法線法線方向)向外在一定范圍內(nèi)隨機伸縮這個點形成單位球體外圍的點唉侄,將這個點和單位球體上同半徑的點連接為線段咒吐。這樣,我們就擁有了近似繪制球體的線段頂點數(shù)據(jù)属划。在渲染時恬叹,針對線段指定代表色彩和透明度的uniform變量即可渲染出一個線段球體。生成頂點數(shù)據(jù)的代碼如下:
vector<Cvec3f> createLinesSphereGeometry(float r){
vector<Cvec3f> vertices;
for(int i = 0; i < 1500; i++) {
//生成范圍為[-1..1]的隨機坐標部件值
float x = rand()/(RAND_MAX/2.0) - 1;
float y = rand()/(RAND_MAX/2.0) - 1;
float z = rand()/(RAND_MAX/2.0) - 1;
Cvec3f vertex(x,y,z);
//標準化后同眯,這些隨機點就變成了單位球體上的點绽昼,經(jīng)過縮放后,可以變成了不同尺寸球面上的點
vertex.normalize();
//縮放為半徑為r的球體
vertex *= r;
vertices.push_back(vertex);
//在當前半徑為r的球體上的點须蜗,被乘以固定方位的隨機因子
Cvec3f vertexOuter = vertex * ((rand()/(float)RAND_MAX) * 0.09 + 1 );
vertices.push_back(vertexOuter);
}
return vertices;
}
球體的多重繪制和移動
webgl_lines_sphere例子中進行了多重(9層)球體繪制硅确,并且讓內(nèi)層的球體隨時間縮放變換,同時進行逆時針旋轉(zhuǎn)明肮,外側(cè)的球體則只進行順時針旋轉(zhuǎn)菱农。還有,內(nèi)外側(cè)球體的色彩和透明度也進行對應(yīng)的設(shè)置柿估。整體的展示效果比較有趣循未。部分代碼如下:
struct RenderingParameter{
float scale;
string color;
float opacity;
};
//球體的初始大小,色彩和透明度信息
vector<RenderingParameter> parameters = {{0.25,"0xff7700", 1}, {0.5, "0xff9900", 1}, {0.75, "0xffaa00", 0.75}, {1, "0xffaa00", 0.5}, {1.25, "0x000833", 0.8},
{3.0, "0xaaaaaa", 0.75}, {3.5,"0xffffff",0.5}, {4.5, "0xffffff", 0.25}, {5.5, "0xffffff", 0.125}};
//球體的初始化過程代碼
shared_ptr<Geometry> geometry;
vector<Cvec3f> lineSphereVertices = createLinesSphereGeometry(300.0);
geometry.reset(new Geometry(&lineSphereVertices[0],(int)lineSphereVertices.size()));
mainCamera.reset(new PerspectiveCamera());
mainCamera->updatePorjectonMatrix();
Cvec3 translationVec = Cvec3(0,0,-550);
for(size_t i=0;i<parameters.size();i++){
shared_ptr<LinesSphereModel> ls = make_shared<LinesSphereModel>(LinesSphereModel(geometry,simpleShaderState));
RenderingParameter para=parameters[i];
//每個球體的初始大小
ls->scale = Cvec3(para.scale,para.scale,para.scale);
originalScale[i]=para.scale;
ls->position = translationVec;
//每個球體的色彩
Cvec3 lColor = hexStringToRGB(para.color);
ls->lineColor = lColor;
//每個球體的初始選擇角度(圍繞y軸)
float angleY = rand()/(RAND_MAX/180.0);
ls->rotation = Cvec3(0,angleY,0);
lsModels.push_back(ls);
}
//每次繪制時球體的運動設(shè)置
//全局變量秫舌,在每幀繪制時增加固定數(shù)量
delta+=0.1;
for(int i=0;i<lsModels.size();i++){
// time_t timeInMill = time(NULL);
// double time = (double)timeInMill/10;
shared_ptr<LinesSphereModel> lsModel = lsModels[i];
//設(shè)置內(nèi)外圈球體的旋轉(zhuǎn)角度
int factor = i<4?(i+1):-(i+1);
long rotationY = delta*(factor);
lsModel->rotation = Cvec3(0,rotationY,0);
//對內(nèi)圈球體在特定范圍內(nèi)以正弦方式循環(huán)縮放
if (i < 5){
float scale = originalScale[i] * (i/5.0 + 1) * (1 + 0.5 * sin(7 * delta * 0.05));
lsModel->scale = Cvec3(scale,scale,scale);
}
lsModel->setPerspectiveCamera(mainCamera);
lsModel->UpdateMatrixWorld();
lsModel->Render();
}