昨天我們加入材質貼圖後,原本只是很多個平行螢幕的正方形,變成以下樣子:
是不是很不錯呢? 只是這裡面的星星只有一種!有點單調,所以我另外找了幾種星星的圖片,
希望能讓宇宙繽紛一點。這就牽涉到你有多種材質在要處理。假設我們總共有五種星星要貼圖,
我們先從fragment shader開始,記得fragment shader可以處理材質貼圖相關的變化。
因為有五種星星,所以我們另外增加了四個sampler2D變數
uniform sampler2D uSampler;
uniform sampler2D uSampler2;
uniform sampler2D uSampler3;
uniform sampler2D uSampler4;
uniform sampler2D uSampler5;
在fragment shader的主程式中,我們可以想見一定要判斷,什麼時候要用哪個材質貼圖,
這就先留到最後再處理,先回到webgl程式。
首先現在材質跟影像都改成陣列,我們也將星星索引存成變數方便辨識
var STAR_TEXTURE_1 = 0;
STAR_TEXTURE_2 = 1,
STAR_TEXTURE_3 = 2,
STAR_TEXTURE_4 = 3,
STAR_TEXTURE_5 = 4;
texture = [];
textureImage = [];
接下來我們將setupTexture 改成 setupTexture(i)
function setupTexture(i)
{
gl.activeTexture(gl.TEXTURE0 + i);
texture[i] = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureImage[i]);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
if( !gl.isTexture(texture[i]) )
{
console.error("Error: Texture is invalid");
}
}
然後將loadTexture 改寫成
function loadTexture()
{
textureImage[0] = new Image();
textureImage[0].onload = function() {
setupTexture(0);
glProgram.samplerUniform = gl.getUniformLocation(glProgram, "uSampler");
gl.uniform1i(glProgram.samplerUniform, 0);
}
textureImage[0].src = "./images/star1.jpg";
textureImage[1] = new Image();
textureImage[1].onload = function() {
setupTexture(1);
glProgram.samplerUniform1 = gl.getUniformLocation(glProgram, "uSampler2");
gl.uniform1i(glProgram.samplerUniform1, 1);
}
textureImage[1].src = "./images/star2.jpg";
textureImage[2] = new Image();
textureImage[2].onload = function() {
setupTexture(2);
glProgram.samplerUniform2 = gl.getUniformLocation(glProgram, "uSampler3");
gl.uniform1i(glProgram.samplerUniform2, 2);
}
textureImage[2].src = "./images/star3.jpg";
textureImage[3] = new Image();
textureImage[3].onload = function() {
setupTexture(3);
glProgram.samplerUniform3 = gl.getUniformLocation(glProgram, "uSampler4");
gl.uniform1i(glProgram.samplerUniform3, 3);
}
textureImage[3].src = "./images/star4.jpg";
textureImage[4] = new Image();
textureImage[4].onload = function() {
setupTexture(4);
glProgram.samplerUniform4 = gl.getUniformLocation(glProgram, "uSampler5");
gl.uniform1i(glProgram.samplerUniform4, 4);
}
textureImage[4].src = "./images/star5.jpg";
}
如此一來設定材質的部分就完成了
接下來,我們要思考怎麼繪圖。
我是這麼想:將亂數產生的星星分成五群,每一群在繪圖時,就用不同的星星貼圖!
但是其實我們不需要產生五組vertices資料,只要產生五組繪圖的index資料!
所以我們將 starFieldIndexBuffer 改成陣列
var starFieldIndexBuffer=[];
然後將 starFiled 輸入的參數中的星星數量number,改成groupNumber 與 number
變成說有幾種星星,然後每一種星星的數量有多少。
產生indice的函式變成
for(var j=0;j<this.groupNumber;j++){
var indiceArray = [];
for(var i=0;i<this.number;i++){
indiceArray.push((j*this.number+i)*4+0);
indiceArray.push((j*this.number+i)*4+1);
indiceArray.push((j*this.number+i)*4+2);
indiceArray.push((j*this.number+i)*4+2);
indiceArray.push((j*this.number+i)*4+3);
indiceArray.push((j*this.number+i)*4+0);
}
this.starFieldIndiceArrayForPlane.push(indiceArray);
}
然後再setupBuffer函式中,改成
for(var i=0;i<starfieldVerticeIndiceArray.length;i++){
starFieldIndexBuffer[i] = gl.createBuffer();
starFieldIndexBuffer[i].number_vertex_points = starfieldVerticeIndiceArray[i].length;
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, starFieldIndexBuffer[i]);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(starfieldVerticeIndiceArray[i]), gl.STATIC_DRAW);
}
最後在drawScene中,將drawElement部分改成
for(var i=0;i<starFieldIndexBuffer.length;i++){
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, starFieldIndexBuffer[i]);
gl.drawElements(gl.TRIANGLES, starFieldIndexBuffer[i].number_vertex_points, gl.UNSIGNED_SHORT, 0);
}
是不是覺得怪怪的,好像少了要跟shader說現在要用什麼材質繪圖的部分。
首先再迴圈中,加入
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
接著回到fragment shader,我們加入一個uniform變數來控制現在要用什麼材質來繪圖
然後將main函式改寫為
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform sampler2D uSampler2;
uniform sampler2D uSampler3;
uniform sampler2D uSampler4;
uniform sampler2D uSampler5;
uniform int textureSwitch;
void main(void) {
if (textureSwitch==1) {
gl_FragColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
}
else if(textureSwitch==2){
gl_FragColor = texture2D(uSampler2, vec2(vTextureCoord.s, vTextureCoord.t));
}
else if(textureSwitch==3){
gl_FragColor = texture2D(uSampler3, vec2(vTextureCoord.s, vTextureCoord.t));
}
else if(textureSwitch==4){
gl_FragColor = texture2D(uSampler4, vec2(vTextureCoord.s, vTextureCoord.t));
}
else if(textureSwitch==5){
gl_FragColor = texture2D(uSampler5, vec2(vTextureCoord.s, vTextureCoord.t));
}
else{
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}
}
註:switch似乎編譯不會過
應該很簡單!接著再drawScene 的迴圈中加入
glProgram.textureSwitch = gl.getUniformLocation(glProgram,"textureSwitch");
for(var i=0;i<starFieldIndexBuffer.length;i++){
gl.bindTexture(gl.TEXTURE_2D, texture[i]);
gl.uniform1i(glProgram.textureSwitch,i+1);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, starFieldIndexBuffer[i]);
gl.drawElements(gl.TRIANGLES, starFieldIndexBuffer[i].number_vertex_points, gl.UNSIGNED_SHORT, 0);
}
先抓取textureSwitch變數的位置,然後再將目前的值傳進去!
到目前為止就大功告成囉!做出來的效果明天揭曉!