視頻網(wǎng)站建設(shè) 方案網(wǎng)絡(luò)營(yíng)銷的類型
本人剛學(xué)OpenGL不久且自學(xué),文中定有代碼、術(shù)語(yǔ)等錯(cuò)誤,歡迎指正
我寫的項(xiàng)目地址:https://github.com/liujianjie/LearnOpenGLProject
文章目錄
- 天空盒
- 介紹
- 如何采樣
- OpenGL紋理目標(biāo)
- 例子0:天空盒效果
- 環(huán)境映射
- 反射
- 例子1:Cube反射
- 例子2:模型反射
- 折射
- 例子1:Cube折射
- 例子2:模型折射
- 測(cè)試-先渲染天空盒再渲染物體,默認(rèn)深度LESS比較方式
天空盒
介紹
立方體貼圖就是一個(gè)包含了6個(gè)2D紋理的紋理,每個(gè)2D紋理都組成了立方體的一個(gè)面:一個(gè)有紋理的立方體。
如何采樣
- 方向向量的大小并不重要,只要提供了方向,OpenGL就會(huì)獲取方向向量(最終)所擊中的紋素,并返回對(duì)應(yīng)的采樣紋理值。
- 只要立方體的中心位于原點(diǎn),我們就能使用立方體的實(shí)際位置向量來(lái)對(duì)立方體貼圖進(jìn)行采樣了。
- 我們可以將所有頂點(diǎn)的紋理坐標(biāo)當(dāng)做是立方體的頂點(diǎn)位置。最終得到的結(jié)果就是可以訪問(wèn)立方體貼圖上正確面(Face)紋理的一個(gè)紋理坐標(biāo)。
立方體有36個(gè)頂點(diǎn)位置,在頂點(diǎn)著色器后每個(gè)片段都有自己的頂點(diǎn)位置,采樣天空盒時(shí)用這個(gè)頂點(diǎn)位置當(dāng)做紋理坐標(biāo)即可。
OpenGL紋理目標(biāo)
紋理目標(biāo) | 方位 |
---|---|
GL_TEXTURE_CUBE_MAP_POSITIVE_X | 右 |
GL_TEXTURE_CUBE_MAP_NEGATIVE_X | 左 |
GL_TEXTURE_CUBE_MAP_POSITIVE_Y | 上 |
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y | 下 |
GL_TEXTURE_CUBE_MAP_POSITIVE_Z | 后 |
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z | 前 |
例子0:天空盒效果
-
加載天空盒
// 加載紋理// ------------- unsigned int cubeTexture = loadTexture(FileSystem::getPath("assest/textures/container.jpg").c_str()); // 加載天空盒 vector<std::string> faces{FileSystem::getPath("assest/textures/skybox/right.jpg"),FileSystem::getPath("assest/textures/skybox/left.jpg"),FileSystem::getPath("assest/textures/skybox/top.jpg"),FileSystem::getPath("assest/textures/skybox/bottom.jpg"),FileSystem::getPath("assest/textures/skybox/front.jpg"),FileSystem::getPath("assest/textures/skybox/back.jpg") }; unsigned int cubemapTexture = loadCubemap(faces); // 加載天空盒 // 加載順序 // order: // +X (right) // -X (left) // +Y (top) // -Y (bottom) // +Z (front) // -Z (back) unsigned int loadCubemap(vector<std::string> faces) {unsigned int textureID;glGenTextures(1, &textureID);glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);int width, height, nrChannels;for (unsigned int i = 0; i < faces.size(); i++) {unsigned char* data = stbi_load(faces[i].c_str(), &width, &height, &nrChannels, 0);if (data) {glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);stbi_image_free(data);}else {std::cout << "Cubemap texture failed to load at path:" << faces[i] << std::endl;stbi_image_free(data);}}glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);;glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);;glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);;glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);;glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);;return textureID; }
-
為天空盒創(chuàng)建立方體的六個(gè)面的頂點(diǎn)數(shù)據(jù)以及VAO VBO
// skybox VAO unsigned int skyboxVAO, skyboxVBO; glGenVertexArrays(1, &skyboxVAO); glGenBuffers(1, &skyboxVBO); glBindVertexArray(skyboxVAO); glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO); glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); glBindVertexArray(0);
-
渲染
glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST);glm::mat4 model = glm::mat4(1.0f); glm::mat4 view = camera.GetViewMatrix(); glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);// 渲染立方體 shader.use(); view = camera.GetViewMatrix(); shader.setMat4("model", model);// 不變,在中心 shader.setMat4("view", view); shader.setMat4("projection", projection); glBindVertexArray(cubeVAO); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, cubeTexture); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0);// 渲染天空盒 // 重點(diǎn)代碼:小于等于。由于深度緩沖區(qū)的默認(rèn)值為1,而到頂點(diǎn)著色器里設(shè)置了天空盒的深度值為1,所以要為小于等于,1=1,測(cè)試才通過(guò)才到片段著色器采樣顏色 glDepthFunc(GL_LEQUAL); skyboxShader.use(); //view = camera.GetViewMatrix(); // 重點(diǎn)代碼:取4x4矩陣左上角的3x3矩陣來(lái)移除變換矩陣的位移部分,再變回4x4矩陣。/// // 防止攝像機(jī)移動(dòng),天空盒會(huì)受到視圖矩陣的影響而改變位置,即攝像機(jī)向z后退,天空盒和cube向z前進(jìn) view = glm::mat4(glm::mat3(camera.GetViewMatrix())); skyboxShader.setMat4("view", view); skyboxShader.setMat4("projection", projection); glBindVertexArray(skyboxVAO); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);// 第一個(gè)參數(shù)從GL_TEXTURE_2D 變?yōu)镚L_TEXTURE_CUBE_MAP glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); glDepthFunc(GL_LESS);
-
glsl和采樣
#version 330 core layout (location = 0) in vec3 aPos;// 紋理坐標(biāo)是3維的 out vec3 TexCoords; // 不用model轉(zhuǎn)換到世界矩陣 uniform mat4 projection; uniform mat4 view; void main() {// 紋理坐標(biāo)等于位置坐標(biāo)/TexCoords = aPos;vec4 pos = projection * view * vec4(aPos, 1.0);// z為w,透視除法除后z=(z=w/w)=1,深度為最遠(yuǎn)///gl_Position = pos.xyww; }
#version 330 core out vec4 FragColor;// 紋理坐標(biāo)是3維的 in vec3 TexCoords;// 紋理坐標(biāo)// 天空盒紋理采樣 uniform samplerCube skybox;void main(){ FragColor = texture(skybox, TexCoords); }
-
關(guān)鍵地方
-
天空盒不會(huì)跟隨攝像機(jī)移動(dòng)
// 重點(diǎn)代碼:取4x4矩陣左上角的3x3矩陣來(lái)移除變換矩陣的位移部分,再變回4x4矩陣。 // 防止攝像機(jī)移動(dòng),天空盒會(huì)受到視圖矩陣的影響而改變位置,即攝像機(jī)向z后退,天空盒和cube向z前進(jìn) view = glm::mat4(glm::mat3(camera.GetViewMatrix()));
-
天空盒后渲染,也不會(huì)覆蓋先前繪制的物體
-
先繪制其它物體
-
設(shè)置深度測(cè)試為小于等于
-
繪制天空盒
在天空盒的頂點(diǎn)著色器運(yùn)行后,會(huì)執(zhí)行透視除法,將gl_Position的
xyz
坐標(biāo)除以w分量,將gl_Position的xyz
坐標(biāo)除以w分量(透視除法所做)。所以我們?cè)O(shè)置天空盒的z為w,z=(z=w/w)=1
gl_Position = pos.xyww;// z為w,透視除法除后z=(z=w/w)=1,深度為最遠(yuǎn)
-
由于深度測(cè)試為小于等于(結(jié)合下面圖示)
-
在其他物體已占據(jù)片段的深度緩沖值<=1
天空盒的深度值1不小于等于這些片段的緩沖值,所以不會(huì)通過(guò)深度測(cè)試,從而保持原有的物體片段顏色。
-
其他物體未占據(jù)片段深度緩沖的默認(rèn)值為1
天空盒的深度值1小于等于深度緩沖的值1,所以會(huì)通過(guò)深度測(cè)試,從而輸出天空盒片段。
-
-
錯(cuò)誤做法,將深度測(cè)試為默認(rèn)的小于
-
其他物體未占據(jù)片段深度緩沖的默認(rèn)值為1
天空盒的深度值1不小于深度緩沖區(qū)的默認(rèn)值1,不會(huì)通過(guò)深度測(cè)試,從而具有天空盒的顏色的片段不會(huì)輸出到屏幕上。
-
-
-
-
效果
環(huán)境映射
-
什么是環(huán)境映射
通過(guò)使用環(huán)境的立方體貼圖,我們可以給物體反射和折射的屬性。
這樣使用環(huán)境立方體貼圖的技術(shù)叫做環(huán)境映射(Environment Mapping),其中最流行的兩個(gè)是反射(Reflection)和折射(Refraction)。
反射
-
原理圖
例子1:Cube反射
-
代碼
立方體的shader,天空盒的shader不變(還是和上面例子:天空盒效果的一樣)
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal;out vec3 Normal; out vec3 Position;uniform mat4 projection; uniform mat4 model; uniform mat4 view; void main() {// 法線矩陣Normal = mat3(transpose(inverse(model))) * aNormal;// 到世界空間Position = vec3(model * vec4(aPos, 1.0));// 這里不再是gl_Position = pos.xyww;因?yàn)檫@是中間立方體的,不是天空盒的shadergl_Position = projection * view * vec4(aPos, 1.0); }
#version 330 core out vec4 FragColor;in vec3 Normal; in vec3 Position; // 片段的坐標(biāo)-世界空間uniform vec3 cameraPos;// 天空盒紋理采樣 uniform samplerCube skybox;void main(){ // 從眼睛位置指向片段位置vec3 I = normalize(Position - cameraPos);vec3 R = reflect(I, normalize(Normal));// 采樣天空盒的uv坐標(biāo)是3維的FragColor = vec4(texture(skybox, R).rgb, 1.0);// FragColor = texture(skybox, R); 這個(gè)效果一樣 }
cpp
Shader shader("assest/shader/4高級(jí)OpenGL/6.2.1.cube-反射天空盒.vs", "assest/shader/4高級(jí)OpenGL/6.2.1.cube-反射天空盒.fs"); Shader skyboxShader("assest/shader/4高級(jí)OpenGL/6.1.1.天空盒-普通效果.vs", "assest/shader/4高級(jí)OpenGL/6.1.1.天空盒-普通效果.fs"); ..... // shader configuration // -------------------- shader.use(); shader.setInt("skybox", 0);skyboxShader.use(); skyboxShader.setInt("skybox", 0);glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);// 第一個(gè)參數(shù)從GL_TEXTURE_2D 變?yōu)镚L_TEXTURE_CUBE_MAP// render loop // ----------- while (!glfwWindowShouldClose(window)) {// 渲染立方體shader.use();view = camera.GetViewMatrix();shader.setMat4("model", model);// 不變,在中心shader.setMat4("view", view);shader.setMat4("projection", projection);// 為了反射傳入shader.setVec3("cameraPos", camera.Position);glBindVertexArray(cubeVAO);glDrawArrays(GL_TRIANGLES, 0, 36);glBindVertexArray(0);// 其它和天空盒的代碼一樣
-
效果
箱子上的貼圖是后面的天空盒貼圖
例子2:模型反射
-
代碼
立方體的shader,天空盒的shader不變(還是和上面例子:天空盒效果的一樣)
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords;out vec3 Normal; out vec3 Position; // 片段的坐標(biāo)-世界空間 out vec2 TexCoords;// 紋理坐標(biāo)uniform mat4 model; uniform mat4 view; uniform mat4 projection;void main() {gl_Position = projection * view * model * vec4(aPos, 1.0);TexCoords = aTexCoords;// 到世界空間Position = vec3(model * vec4(aPos, 1.0));// 這里不再是gl_Position = pos.xyww;因?yàn)檫@是中間立方體的,不是天空盒的shaderNormal = mat3(transpose(inverse(model))) * aNormal; }
#version 330 core out vec4 FragColor;in vec3 Normal; in vec3 Position; // 片段的坐標(biāo)-世界空間 in vec2 TexCoords;// 紋理坐標(biāo)uniform vec3 cameraPos; uniform sampler2D texture_diffuse1; uniform sampler2D texture_specular1; uniform sampler2D texture_height1;// 天空盒紋理采樣 uniform samplerCube skybox;void main(){ vec3 I = normalize(Position - cameraPos);vec3 R = reflect(I, normalize(Normal));// 采樣鏡面光貼圖顏色(uv坐標(biāo)是2維的)vec4 specular4 = texture(texture_specular1, TexCoords); // 采樣出來(lái)的顏色是4維的vec3 specular3 = specular4.rgb;// 采樣天空盒顏色(uv坐標(biāo)是3維的)并乘以鏡面光貼圖顏色FragColor = vec4(texture(skybox, R).rgb * specular3, 1.0) ;// FragColor = vec4(texture(skybox, R).rgb, 1.0) ;// 未乘以鏡面光貼圖顏色 }
cpp
// 加載模型 Model ourModel(FileSystem::getPath("assest/model/nanosuit/nanosuit.obj"));// shader configuration // -------------------- shader.use(); shader.setInt("skybox", 4);skyboxShader.use(); skyboxShader.setInt("skybox", 4); // 設(shè)置的天空盒的紋理單元位置,好像不會(huì)與普通的紋理沖突,但保險(xiǎn)起見(jiàn)還是設(shè)為4 glActiveTexture(GL_TEXTURE4); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);// 第一個(gè)參數(shù)從GL_TEXTURE_2D 變?yōu)镚L_TEXTURE_CUBE_MAPwhile (!glfwWindowShouldClose(window)) {// 渲染這個(gè)模型// 為了反射傳入model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));model = glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f));shader.use();shader.setVec3("cameraPos", camera.Position);shader.setMat4("model", model);shader.setMat4("view", view);shader.setMat4("projection", projection);ourModel.Draw(shader); }
-
效果
采樣天空盒顏色,未乘以鏡面光貼圖顏色
采樣天空盒顏色,并乘以鏡面光貼圖顏色
折射
-
原理
-
折射率表
材質(zhì) 折射率 空氣 1.00 水 1.33 冰 1.309 玻璃 1.52 鉆石 2.42 例子中,光線/視線從空氣(折射率1)進(jìn)入玻璃(如果我們假設(shè)箱子是玻璃制的),所以比值為1.00/1.52=0.658
例子1:Cube折射
-
代碼
和反射的代碼差不多,就是中間立方體的glsl片段著色器代碼不一樣
void main(){ float ratio = 1.00 / 1.52;vec3 I = normalize(Position - cameraPos);vec3 R = refract(I, normalize(Normal), ratio);// refract,第三個(gè)參數(shù)是折射率// 采樣天空盒顏色(uv坐標(biāo)是3維的)FragColor = vec4(texture(skybox, R).rgb, 1.0);// FragColor = texture(skybox, R); 這個(gè)效果一樣 }
-
效果
例子2:模型折射
-
代碼
void main(){ float ratio = 1.00 / 1.52;vec3 I = normalize(Position - cameraPos);vec3 R = refract(I, normalize(Normal), ratio);// refract,第三個(gè)參數(shù)是折射率// 采樣天空盒顏色(uv坐標(biāo)是3維的)FragColor = vec4(texture(skybox, R).rgb, 1.0);// FragColor = texture(skybox, R); 這個(gè)效果一樣 }
-
效果
測(cè)試-先渲染天空盒再渲染物體,默認(rèn)深度LESS比較方式
-
代碼
// 將天空盒的盒子長(zhǎng)寬為20 float skyboxVertices[] = {// positions -10.0f, 10.0f, -10.0f,-10.0f, -10.0f, -10.0f,..... }; // 渲染天空盒 //glDepthFunc(GL_LEQUAL); // 不用LEQUAL而是默認(rèn)的LESS skyboxShader.use(); // 重點(diǎn)代碼:取4x4矩陣左上角的3x3矩陣來(lái)移除變換矩陣的位移部分,再變回4x4矩陣。 // 防止攝像機(jī)移動(dòng),天空盒會(huì)受到視圖矩陣的影響而改變位置,即攝像機(jī)向z后退,天空盒和cube向z前進(jìn) view = glm::mat4(glm::mat3(camera.GetViewMatrix())); skyboxShader.setMat4("view", view); skyboxShader.setMat4("projection", projection); glBindVertexArray(skyboxVAO); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, cubemapTexture);// 第一個(gè)參數(shù)從GL_TEXTURE_2D 變?yōu)镚L_TEXTURE_CUBE_MAP glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); //glDepthFunc(GL_LESS);// 渲染立方體 shader.use(); view = camera.GetViewMatrix(); shader.setMat4("model", model);// 不變,在中心 shader.setMat4("view", view); shader.setMat4("projection", projection); glBindVertexArray(cubeVAO); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, cubeTexture); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0);
天空盒的頂點(diǎn)位置z透視除法后不為1
//gl_Position = pos.xyww;// z=w,透視除法除后還是1,深度為最遠(yuǎn) gl_Position= projection * view * vec4(aPos, 1.0);
-
解釋代碼順序
-
天空盒的盒子長(zhǎng)寬為20
-
先繪制天空盒,再繪制箱子
-
這代碼天空盒將不會(huì)受攝像機(jī)的觀察矩陣的位移部分影響
所以雖然glsl天空盒的深度值z(mì)未設(shè)置w,但是視覺(jué)上依舊是無(wú)限遠(yuǎn)
不過(guò)實(shí)際上現(xiàn)在代碼造成的影響是,不論攝像機(jī)所在什么位置,以攝像機(jī)為原點(diǎn),處在一個(gè)20*20大小的立方體盒子里,在20*20范圍內(nèi)的物體被顯示,20*20外的物體被天空盒顏色所覆蓋。
換句話說(shuō):注意攝像機(jī)在原點(diǎn),所以20*20的盒子半徑為10,于是原點(diǎn)為出發(fā)點(diǎn)距離攝像機(jī)長(zhǎng)度小于10的物體會(huì)顯示,大于10的物體會(huì)被天空盒顏色所覆蓋。
-
-
進(jìn)一步解釋(結(jié)合下方圖)
-
箱子離攝像機(jī)的距離 <10(第一幅圖)
箱子的深度值小于天空盒,所以天空盒同箱子所占的片段區(qū)域會(huì)被丟棄,顯示箱子的片段顏色
-
箱子離攝像機(jī)的距離 > 10(第二幅圖)
箱子的深度值大于天空盒,所以天空盒同箱子所占的片段區(qū)域會(huì)覆蓋箱子,顯示天空盒的片段顏色
-