全景又被稱為3D實景案狠,是一種新興的富媒體技術(shù),其與視頻卷雕,聲音节猿,圖片等傳統(tǒng)的流媒體最大的區(qū)別是“可操作,可交互”爽蝴。 全景分為虛擬現(xiàn)實和3D實景兩種沐批。虛擬現(xiàn)實是利用maya等軟件,制作出來的模擬現(xiàn)實的場景蝎亚,代表有虛擬紫禁城等;3D實景是利用單反相機或街景車拍攝實景照片先馆,經(jīng)過特殊的拼合发框,處理,讓作者立于畫境中煤墙,讓最美的一面展現(xiàn)出來梅惯。
全景顧名思義就是給人以三維立體感覺的實景360度全方位圖像~
此圖像最大的三個特點是:
1、全:全方位仿野,全面的展示了360度球型范圍內(nèi)的所有景致铣减;可在例子中用鼠標(biāo)左鍵按住拖動,觀看場景的各個方向脚作;
2葫哗、景:實景,真實的場景球涛,三維實景大多是在照片基礎(chǔ)之上拼合得到的圖像劣针,最大限度的保留了場景的真實性;
3亿扁、360:360度環(huán)視的效果捺典,雖然照片都是平面的,但是通過軟件處理之后得到的360度實景从祝,卻能給人以三維立體的空間感覺襟己,使觀者猶如身在其中引谜。全景由于它給人們帶來全新的真實現(xiàn)場感和交互式的感受。它可廣泛應(yīng)用于三維電子商務(wù)擎浴,如在線的房地產(chǎn)樓盤展示煌张、虛擬旅游、虛擬教育等領(lǐng)域退客。
本篇我們基于上一篇粒子光束 的基礎(chǔ)上實現(xiàn)全景背景圖
看效果圖:
我們用連續(xù)的6張?zhí)炜請D片骏融,拼接成了一個無縫的立方體。想想一下我們站在這個立方體的中心萌狂,這個時候我們的前后左右上下都充滿了天空的圖片档玻,不管你的頭轉(zhuǎn)向哪邊,都能夠看見天空茫藏。
理論上我們把眼睛旋轉(zhuǎn)360度觀察误趴,圖上的三個光束會先消失在出現(xiàn),這就像是我們把立方體旋轉(zhuǎn)了360度又回到了原位置一樣务傲。就像下圖:
之所以能實現(xiàn)360度旋轉(zhuǎn)凉当,是因為我們用了6張圖片并把他們加載成一個立方體。
我們先創(chuàng)建一個模型對象類售葡,即立方體模型看杭。
public class Skybox {
private static final int POSITION_COMPONENT_COUNT = 3;
private final VertexArray vertexArray;
private final ByteBuffer indexArray;
public Skybox() {
// Create a unit cube.
vertexArray = new VertexArray(new float[] {
-1, 1, 1, // (0) Top-left near
1, 1, 1, // (1) Top-right near
-1, -1, 1, // (2) Bottom-left near
1, -1, 1, // (3) Bottom-right near
-1, 1, -1, // (4) Top-left far
1, 1, -1, // (5) Top-right far
-1, -1, -1, // (6) Bottom-left far
1, -1, -1 // (7) Bottom-right far
});
// 6 indices per cube side
indexArray = ByteBuffer.allocateDirect(6 * 6)
.put(new byte[] {
// Front
1, 3, 0,
0, 3, 2,
// Back
4, 6, 5,
5, 6, 7,
// Left
0, 2, 4,
4, 2, 6,
// Right
5, 7, 1,
1, 7, 3,
// Top
5, 1, 4,
4, 1, 0,
// Bottom
6, 2, 7,
7, 2, 3
});
indexArray.position(0);
}
public void bindData(SkyboxShaderProgram skyboxProgram) {
vertexArray.setVertexAttribPointer(0,
skyboxProgram.getPositionAttributeLocation(),
POSITION_COMPONENT_COUNT, 0);
}
public void draw() {
glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indexArray);
}
}
我們用VertexArray儲存立方體的8個頂點。用indexArray 這個索引數(shù)組的索引指向每個頂點挟伙,把所有頂點分別綁定成三角形組楼雹,每個組有立方體上每個面的2個三角形。
bindData方法從內(nèi)存中加載數(shù)據(jù)綁定尖阔,然后通過 glDrawElements(GL_TRIANGLES, 36, GL_UNSIGNED_BYTE, indexArray);繪制立方體贮缅。
下面我們添加著色器
頂點著色器
uniform mat4 u_Matrix;
attribute vec3 a_Position;
varying vec3 v_Position;
void main()
{
v_Position = a_Position; //把頂點位置傳給片段著色器
v_Position.z = -v_Position.z; //反轉(zhuǎn)Z分量。把右手坐標(biāo)系轉(zhuǎn)化為左手坐標(biāo)系
gl_Position = u_Matrix * vec4(a_Position, 1.0);//成u_Matrix即用投影~
gl_Position = gl_Position.xyww;//把Z值變成W介却,這樣透視除法之后為1谴供,即Z始終在1的遠平面上。Z=1最遠齿坷,即在別的物體的后面桂肌,就像是背景。
}
片段著色器:
precision mediump float;
uniform samplerCube u_TextureUnit;//立方體紋理
varying vec3 v_Position;
void main()
{
gl_FragColor = textureCube(u_TextureUnit, v_Position);
}
然后用Java代碼封裝著色器程序
這里用java代碼映射到著色器上
uMatrixLocation = glGetUniformLocation(program, U_MATRIX);
uTextureUnitLocation = glGetUniformLocation(program, U_TEXTURE_UNIT);
aPositionLocation = glGetAttribLocation(program, A_POSITION);
}
public void setUniforms(float[] matrix, int textureId) {
glUniformMatrix4fv(uMatrixLocation, 1, false, matrix, 0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureId);
glUniform1i(uTextureUnitLocation, 0);
}
有了關(guān)系映射胃夏,就可以綁定數(shù)據(jù)進行繪制了
@onDrawFrame(GL10 gl10)中
skyboxProgram.useProgram();
skyboxProgram.setUniforms(viewProjectionMatrix, skyboxTexture);//映射
skybox.bindData(skyboxProgram);//綁定數(shù)據(jù)
skybox.draw();//繪制
下面看手勢操作代碼
在Activity中監(jiān)聽glSurfaceView
glSurfaceView.setOnTouchListener(new View.OnTouchListener() {
float previousX, previousY;
@Override public boolean onTouch(View v, MotionEvent event) {
if (event != null) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
previousX = event.getX();
previousY = event.getY();
} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
final float deltaX = event.getX() - previousX;
final float deltaY = event.getY() - previousY;
previousX = event.getX();
previousY = event.getY();
glSurfaceView.queueEvent(new Runnable() {
@Override public void run() {
particlesRenderer.handleTouchDrag(deltaX, deltaY);
}
});
}
return true;
} else {
return false;
}
}
});
因為openGL是在一個單獨的線程中的轴或,所以需要 glSurfaceView.queueEvent發(fā)送事件
把deltaX, deltaY傳遞到了Renderer類中
public void handleTouchDrag(float deltaX, float deltaY) {
xRotation += deltaX / 16f; //除以16是縮減拖動效果的
yRotation += deltaY / 16f;
然后我們根據(jù)這個滑動值,用矩陣去操作立方體變化仰禀。
@onDrawFrame
//以 0 0 0為中心繪制照雁,我們站在中心觀察
private void drawSkybox() {
setIdentityM(viewMatrix, 0);
rotateM(viewMatrix, 0, -yRotation, 1f, 0f, 0f);//沿著Y軸旋轉(zhuǎn)
rotateM(viewMatrix, 0, -xRotation, 0f, 1f, 0f);//沿著x軸旋轉(zhuǎn) FPS模型
multiplyMM(viewProjectionMatrix, 0, projectionMatrix, 0, viewMatrix, 0);
skyboxProgram.useProgram();
skyboxProgram.setUniforms(viewProjectionMatrix, skyboxTexture);
skybox.bindData(skyboxProgram);
skybox.draw();
}
在這之前我們要在onSurfaceCreated里面初始化立方體
skyboxProgram = new SkyboxShaderProgram(context);
skybox = new Skybox();
@Override public void onSurfaceChanged(GL10 gl10, int width, int height) {
GLES20.glViewport(0, 0, width, height);
MatrixHelper.perspectiveM(projectionMatrix, 45, (float) width / (float) height, 1f, 10f);
}
ps:
Kotlin可以編譯成Java字節(jié)碼,也可以編譯成JavaScript,方便在沒有JVM的設(shè)備上運行饺蚊,最近發(fā)布了Kotlin/Native能把Kotlin編譯成機器碼萍诱,也就是C/C++一樣的能力。本專題專注Kotlin污呼,Kotlin/Native裕坊,KotlinJS與Kotlin_Android的那些事,讓我們共同學(xué)習(xí)Kotlin壯大Kotlin~
加入專題吧