本节我们一起来学习一下材质的知识,看完本节的内容后,我们应该总结到,最终实现的效果是在上一节的基础上,把所有影响因素慢慢都加进来,考虑的更细致得到的结果。上一节我们考虑的是光的反射因素,把环境光照、漫反射光照、镜面反射光照计算到最终效果上;本节继续把物体的材质、光源的环境光照分量、漫反射分量、镜面反射分量全部都考虑进来,这样就会越来越接近实际现象。
本节最终实现的效果如下:
为了方便对比,我们画了四个立方体,每次的视口坐标调用glViewport转换了一下,关于glViewport API的使用,可以参考Opengl ES系列学习--glViewport API使用一文。左上角颜色不断变化的就是综合了物体材质和光源的三个分量的效果,且光源的分量不断变化;右上角的是光源分量固定不变的效果;左下角的是只有材质属性,未考虑光源分量影响的效果;右下角的是和上一节相同的效果,只考虑了冯氏光照模型的因素。
加上我们的箱子和笑脸纹理就是这样的效果。
先来看一下GlMaterialRender类,完整源码如下:
package com.opengl.learn.aric.material;
import android.content.Context;
import android.opengl.GLES32;
import android.opengl.GLSurfaceView;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import static android.opengl.GLES20.GL_COLOR_BUFFER_BIT;
import static android.opengl.GLES20.GL_DEPTH_BUFFER_BIT;
import static android.opengl.GLES20.GL_DEPTH_TEST;
public class GlMaterialRender implements GLSurfaceView.Renderer {
private static final String TAG = GlMaterialRender.class.getSimpleName();
private Context mContext;
private int mWidth, mHeight;
private MaterialCube mCube;
private MaterialLight mLight;
public GlMaterialRender(Context context) {
mContext = context;
mCube = new MaterialCube(mContext);
mLight = new MaterialLight(mContext);
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
mCube.onSurfaceCreated();
mLight.onSurfaceCreated();
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
mWidth = width;
mHeight = height;
mCube.onSurfaceChanged(gl, width, height);
mLight.onSurfaceChanged(gl, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES32.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
GLES32.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GLES32.glEnable(GL_DEPTH_TEST);
for (int i = 0; i < 4; i++) {
if (i == 0) {
GLES32.glViewport(0, mHeight / 2, mWidth / 2, mHeight / 2);
} else if (i == 1) {
GLES32.glViewport(mWidth / 2, mHeight / 2, mWidth / 2, mHeight / 2);
} else if (i == 2) {
GLES32.glViewport(0, 0, mWidth / 2, mHeight / 2);
} else if (i == 3) {
GLES32.glViewport(mWidth / 2, 0, mWidth / 2, mHeight / 2);
}
mCube.onDrawFrame(i);
mLight.onDrawFrame();
}
}
}
就是在onDrawFrame方法中不断变换视口去绘制,然后把变量 i 传到MaterialCube类中方便区分,其他的就没有什么特殊的地方了。绘制光源的MaterialLight和上一节的代码完全相同,没有任何修改,光源的顶点和片段着色器的代码也是完全相同,我们就不重复贴了。接着看一下立方体MaterialCube,完整源码如下:
package com.opengl.learn.aric.material;
import android.content.Context;
import android.opengl.GLES32;
import android.opengl.Matrix;
import android.util.Log;
import com.opengl.learn.OpenGLUtils;
import com.opengl.learn.R;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.ShortBuffer;
import javax.microedition.khronos.opengles.GL10;
import static android.opengl.GLES20.GL_ARRAY_BUFFER;
import static android.opengl.GLES20.GL_FLOAT;
import static android.opengl.GLES20.GL_STATIC_DRAW;
import static android.opengl.GLES20.GL_TEXTURE0;
import static android.opengl.GLES20.GL_TEXTURE1;
import static android.opengl.GLES20.GL_TEXTURE_2D;
import static android.opengl.GLES20.GL_TRIANGLES;
import static android.opengl.GLES20.glGenBuffers;
import static android.opengl.GLES20.glGetUniformLocation;
public class MaterialCube {
private final float[] mVerticesData =
{
// back face
0.5f, 0.5f, -0.5f, // (5) Top-right far
0.5f, -0.5f, -0.5f, // (7) Bottom-right far
-0.5f, -0.5f, -0.5f, // (6) Bottom-left far
-0.5f, -0.5f, -0.5f, // (6) Bottom-left far
-0.5f, 0.5f, -0.5f, // (4) Top-left far
0.5f, 0.5f, -0.5f, // (5) Top-right far
// front face
-0.5f, 0.5f, 0.5f, // (0) Top-left near
-0.5f, -0.5f, 0.5f, // (2) Bottom-left near
0.5f, -0.5f, 0.5f, // (3) Bottom-right near
0.5f, -0.5f, 0.5f, // (3) Bottom-right near
0.5f, 0.5f, 0.5f, // (1) Top-right near
-0.5f, 0.5f, 0.5f, // (0) Top-left near
// left face
-0.5f, 0.5f, -0.5f, // (4) Top-left far
-0.5f, -0.5f, -0.5f, // (6) Bottom-left far
-0.5f, -0.5f, 0.5f, // (2) Bottom-left near
-0.5f, -0.5f, 0.5f, // (2) Bottom-left near
-0.5f, 0.5f, 0.5f, // (0) Top-left near
-0.5f, 0.5f, -0.5f, // (4) Top-left far
// right face
0.5f, 0.5f, 0.5f, // (1) Top-right near
0.5f, -0.5f, 0.5f, // (3) Bottom-right near
0.5f, -0.5f, -0.5f, // (7) Bottom-right far
0.5f, -0.5f, -0.5f, // (7) Bottom-right far
0.5f, 0.5f, -0.5f, // (5) Top-right far
0.5f, 0.5f, 0.5f, // (1) Top-right near
// bottom face
-0.5f, -0.5f, 0.5f, // (2) Bottom-left near
-0.5f, -0.5f, -0.5f, // (6) Bottom-left far
0.5f, -0.5f, -0.5f, // (7) Bottom-right far
0.5f, -0.5f, -0.5f, // (7) Bottom-right far
0.5f, -0.5f, 0.5f, // (3) Bottom-right near
-0.5f, -0.5f, 0.5f, // (2) Bottom-left near
// top face
-0.5f, 0.5f, -0.5f, // (4) Top-left far
-0.5f, 0.5f, 0.5f, // (0) Top-left near
0.5f, 0.5f, 0.5f, // (1) Top-right near
0.5f, 0.5f, 0.5f, // (1) Top-right near
0.5f, 0.5f, -0.5f, // (5) Top-right far
-0.5f, 0.5f, -0.5f, // (4) Top-left far
};
private final float[] mColorsData =
{
1.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 1.0f,
1.0f, 1.0f, 0.0f,
};
private final float[] mTextureData =
{
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
0.0f, 0.0f,
0.0f, 1.0f,
1.0f, 1.0f,
1.0f, 1.0f,
1.0f, 0.0f,
0.0f, 0.0f,
};
private final float[] mNormalData =
{
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
-1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, -1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
0.0f, 1.0f, 0.0f,
};
private final float[] mEyePosData = {0f, 2f, 20f};
private float[] mMateriallightColor = {1.0f, 1.0f, 1.0f};
private static final String TAG = MaterialCube.class.getSimpleName();
private static final int BYTES_PER_FLOAT = 4;
private static final int BYTES_PER_SHORT = 2;
private static final int POSITION_COMPONENT_COUNT = 3;
private static final int COLOR_COMPONENT_COUNT = 3;
private static final int TEXTURE_COMPONENT_COUNT = 2;
private static final int INDEX_COMPONENT_COUNT = 1;
private static final int NORMAL_COMPONENT_COUNT = 3;
private static final int MATRIX_LENGHT = 16;
private Context mContext;
private int mProgramObject;
private int uTextureContainer, containerTexture;
private int uTextureFace, faceTexture;
private int uModelMatrix, uViewMatrix, uProjectionMatrix, uLightColor, uLightPos, uViewPos, uIndex;
private int materialAmbient, materialDiffuse, materialSpecular, materialShininess;
private int lightAmbient, lightDiffuse, lightSpecular;
private FloatBuffer mVerticesBuffer;
private FloatBuffer mColorsBuffer;
private FloatBuffer mTextureBuffer;
private FloatBuffer mNormalBuffer;
private ShortBuffer mIndicesBuffer;
private int mWidth, mHeight;
private int mVAO, mVBO, mCBO, mTBO, mNBO;
private float[] modelMatrix = new float[MATRIX_LENGHT];
private float[] viewMatrix = new float[MATRIX_LENGHT];
private float[] projectionMatrix = new float[MATRIX_LENGHT];
private float radius = 30f;
private long startTime = 0;
private float[][] directions = {
{0f, 0.0f, -20f}, {2f, 0.0f, -20f}, {0f, 4.0f, -20f}, {0f, 4.2f, -20.5f}, {3.24f, -3.4f, -15.5f},
{-3.1f, 2.0f, -7.5f}, {4.47f, -2.0f, -5.5f}, {-4.5f, 2.40f, -9.5f}, {5f, 0.2f, -20.5f}, {-5.43f, -2.3f, -8.5f},
};
public MaterialCube(Context context) {
mContext = context;
mVerticesBuffer = ByteBuffer.allocateDirect(mVerticesData.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mVerticesBuffer.put(mVerticesData).position(0);
mColorsBuffer = ByteBuffer.allocateDirect(mColorsData.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mColorsBuffer.put(mColorsData).position(0);
mTextureBuffer = ByteBuffer.allocateDirect(mTextureData.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mTextureBuffer.put(mTextureData).position(0);
mNormalBuffer = ByteBuffer.allocateDirect(mNormalData.length * BYTES_PER_FLOAT)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mNormalBuffer.put(mNormalData).position(0);
}
public void onSurfaceCreated() {
mProgramObject = OpenGLUtils.loadProgram(mContext, R.raw.glmaterial_cube_vertex, R.raw.glmaterial_cube_fragment);
uTextureContainer = glGetUniformLocation(mProgramObject, "uTextureContainer");
uTextureFace = glGetUniformLocation(mProgramObject, "uTextureFace");
uModelMatrix = glGetUniformLocation(mProgramObject, "uModelMatrix");
uViewMatrix = glGetUniformLocation(mProgramObject, "uViewMatrix");
uProjectionMatrix = glGetUniformLocation(mProgramObject, "uProjectionMatrix");
uLightColor = glGetUniformLocation(mProgramObject, "uLightColor");
uLightPos = glGetUniformLocation(mProgramObject, "uLightPos");
uViewPos = glGetUniformLocation(mProgramObject, "uViewPos");
uIndex = glGetUniformLocation(mProgramObject, "uIndex");
materialAmbient = glGetUniformLocation(mProgramObject, "material.ambient");
materialDiffuse = glGetUniformLocation(mProgramObject, "material.diffuse");
materialSpecular = glGetUniformLocation(mProgramObject, "material.specular");
materialShininess = glGetUniformLocation(mProgramObject, "material.shininess");
lightAmbient = glGetUniformLocation(mProgramObject, "light.ambient");
lightDiffuse = glGetUniformLocation(mProgramObject, "light.diffuse");
lightSpecular = glGetUniformLocation(mProgramObject, "light.specular");
int[] array = new int[1];
GLES32.glGenVertexArrays(1, array, 0);
mVAO = array[0];
array = new int[4];
glGenBuffers(4, array, 0);
mVBO = array[0];
mCBO = array[1];
mTBO = array[2];
mNBO = array[3];
Log.e(TAG, "onSurfaceCreated, " + mProgramObject + ", uTexture: " + uTextureContainer + ", uTextureFace: " + uTextureFace);
containerTexture = OpenGLUtils.loadTexture(mContext, R.mipmap.container);
faceTexture = OpenGLUtils.loadTexture(mContext, R.mipmap.awesomeface);
GLES32.glBindVertexArray(mVAO);
mVerticesBuffer.position(0);
GLES32.glBindBuffer(GL_ARRAY_BUFFER, mVBO);
GLES32.glBufferData(GL_ARRAY_BUFFER, BYTES_PER_FLOAT * mVerticesData.length, mVerticesBuffer, GL_STATIC_DRAW);
GLES32.glVertexAttribPointer(0, POSITION_COMPONENT_COUNT, GL_FLOAT, false, 0, 0);
GLES32.glEnableVertexAttribArray(0);
mTextureBuffer.position(0);
GLES32.glBindBuffer(GL_ARRAY_BUFFER, mTBO);
GLES32.glBufferData(GL_ARRAY_BUFFER, BYTES_PER_FLOAT * mTextureData.length, mTextureBuffer, GL_STATIC_DRAW);
GLES32.glVertexAttribPointer(2, TEXTURE_COMPONENT_COUNT, GL_FLOAT, false, 0, 0);
GLES32.glEnableVertexAttribArray(2);
mNormalBuffer.position(0);
GLES32.glBindBuffer(GL_ARRAY_BUFFER, mNBO);
GLES32.glBufferData(GL_ARRAY_BUFFER, BYTES_PER_FLOAT * mNormalData.length, mNormalBuffer, GL_STATIC_DRAW);
GLES32.glVertexAttribPointer(3, NORMAL_COMPONENT_COUNT, GL_FLOAT, false, 0, 0);
GLES32.glEnableVertexAttribArray(3);
}
public void onSurfaceChanged(GL10 gl, int width, int height) {
mWidth = width;
mHeight = height;
}
public void onDrawFrame(int index) {
if (startTime == 0) {
startTime = System.currentTimeMillis();
}
GLES32.glUseProgram(mProgramObject);
GLES32.glEnableVertexAttribArray(0);
GLES32.glEnableVertexAttribArray(1);
GLES32.glEnableVertexAttribArray(2);
GLES32.glEnableVertexAttribArray(3);
GLES32.glActiveTexture(GL_TEXTURE0);
GLES32.glBindTexture(GL_TEXTURE_2D, containerTexture);
GLES32.glUniform1i(uTextureContainer, 0);
GLES32.glActiveTexture(GL_TEXTURE1);
GLES32.glBindTexture(GL_TEXTURE_2D, faceTexture);
GLES32.glUniform1i(uTextureFace, 1);
GLES32.glUniform3fv(uLightColor, 1, MaterialLight.mLightColorsData, 0);
GLES32.glUniform3fv(uLightPos, 1, MaterialLight.mLightPosData, 0);
GLES32.glUniform3fv(uViewPos, 1, mEyePosData, 0);
GLES32.glUniform3f(materialAmbient, 1.0f, 0.5f, 0.31f);
GLES32.glUniform3f(materialDiffuse, 1.0f, 0.5f, 0.31f);
GLES32.glUniform3f(materialSpecular, 0.5f, 0.5f, 0.5f);
GLES32.glUniform1f(materialShininess, 32.0f);
if (index == 0) {
long now = System.currentTimeMillis();
mMateriallightColor[0] = (float) Math.sin((now - startTime) / 1000f * 2.0f);
mMateriallightColor[1] = (float) Math.sin((now - startTime) / 1000f * 0.7f);
mMateriallightColor[2] = (float) Math.sin((now - startTime) / 1000f * 1.3f);
} else {
mMateriallightColor = new float[]{1.0f, 1.0f, 1.0f};
}
GLES32.glUniform3f(lightAmbient, mMateriallightColor[0] * 0.2f, mMateriallightColor[1] * 0.2f, mMateriallightColor[2] * 0.2f);
GLES32.glUniform3f(lightDiffuse, mMateriallightColor[0] * 0.5f, mMateriallightColor[1] * 0.5f, mMateriallightColor[2] * 0.5f);
GLES32.glUniform3f(lightSpecular, 1.0f, 1.0f, 1.0f);
GLES32.glBindVertexArray(mVAO);
setCubeMatrix(0);
GLES32.glUniformMatrix4fv(uModelMatrix, 1, false, modelMatrix, 0);
GLES32.glUniformMatrix4fv(uViewMatrix, 1, false, viewMatrix, 0);
GLES32.glUniformMatrix4fv(uProjectionMatrix, 1, false, projectionMatrix, 0);
GLES32.glUniform1i(uIndex, index);
GLES32.glDrawArrays(GL_TRIANGLES, 0, mVerticesData.length);
GLES32.glBindVertexArray(0);
GLES32.glDisableVertexAttribArray(0);
GLES32.glDisableVertexAttribArray(1);
GLES32.glDisableVertexAttribArray(2);
GLES32.glDisableVertexAttribArray(3);
}
private void setCubeMatrix(int i) {
Matrix.setIdentityM(modelMatrix, 0);
Matrix.setIdentityM(viewMatrix, 0);
Matrix.setIdentityM(projectionMatrix, 0);
float aspect = (float) mWidth / (float) mHeight;
Matrix.perspectiveM(projectionMatrix, 0, 45f, aspect, 1.0f, 100f);
long now = System.currentTimeMillis();
float camX = (float) (Math.sin((now - startTime) / 1000f) * radius);
float camZ = (float) (Math.cos((now - startTime) / 1000f) * radius);
Matrix.setLookAtM(viewMatrix, 0, mEyePosData[0], mEyePosData[1], mEyePosData[2], 0f, 0f, 0f, 0f, 1f, 0f);
Matrix.translateM(modelMatrix, 0, directions[i][0], directions[i][1], directions[i][2]);
Matrix.scaleM(modelMatrix, 0, 8f, 8f, 8f);
Matrix.rotateM(modelMatrix, 0, -20, 0, 1f, 0);
}
}
和上一节相比,因为我们使用物体材质和光源分量,所以多了七个uniform变量,都需要传值到片段着色器中:material.ambient表示物体材质对环境光照的影响分量;material.diffuse表示物体材质对漫反射的影响分量;material.specular表示对镜面反射的影响分量;material.shininess表示对镜面反射的高光半径;light.ambient表示光源的环境分量;light.diffuse表示光源的漫反射分量;light.specular表示光源的镜面反射分量。考虑的更细致,我们才能控制的更细,效果也会更丰富。
剩下其他的逻辑也就没有什么特殊的地方了,主要就是计算出这七个变量的值,传递到片段着色器中使用。顶点着色器和上一节也是完全相同,最后我们来看一下片段着色器,源码如下:
#version 320 es
precision mediump float;
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
vec4 objectColor;
out vec4 FragColor;
in vec3 outColor;
in vec2 outTexCoord;
in vec3 outNormal;
in vec3 outFragPos;
uniform sampler2D uTextureContainer;
uniform sampler2D uTextureFace;
uniform vec3 uLightColor;
uniform vec3 uLightPos;
uniform vec3 uViewPos;
uniform int uIndex;
uniform Material material;
uniform Light light;
vec4 getAmbientFactor() {
float ambientStrength = 0.2f;
vec4 ambient = vec4(0.0);
if ((uIndex == 0) || (uIndex == 1)) {
ambient = vec4(ambientStrength * material.ambient * light.ambient, 1.0f);
} else if (uIndex == 2) {
ambient = vec4(ambientStrength * vec3(objectColor) * uLightColor, 1.0f);
} else if (uIndex == 3) {
ambient = vec4(ambientStrength * uLightColor, 1.0f);
}
return ambient;
}
vec4 getDiffuseFactor() {
vec3 normal = normalize(outNormal);
vec3 lightDir = normalize(uLightPos - outFragPos);
float diff = max(dot(normal, lightDir), 0.0);
vec4 diffuse = vec4(0.0);
if ((uIndex == 0) || (uIndex == 1)) {
diffuse = vec4(diff * material.diffuse * light.diffuse, 1.0f);
} else if (uIndex == 2) {
diffuse = vec4(diff * vec3(objectColor) * uLightColor, 1.0f);
} else if (uIndex == 3) {
diffuse = vec4(diff * uLightColor, 1.0f);
}
return diffuse;
}
vec4 getSpecularFactor() {
vec3 viewDir = normalize(uViewPos - outFragPos);
vec3 lightDir = normalize(uLightPos - outFragPos);
vec3 reflectDir = reflect(-lightDir, outNormal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0f), material.shininess);
vec4 specular = vec4(0.0);
if ((uIndex == 0) || (uIndex == 1)) {
specular = vec4(spec * material.specular * light.specular, 1.0f);
} else if (uIndex == 2) {
specular = vec4(spec * material.specular * uLightColor, 1.0f);
} else if (uIndex == 3) {
specular = vec4(spec * uLightColor, 1.0f);
}
return specular;
}
void main() {
objectColor = mix(texture(uTextureContainer, outTexCoord), texture(uTextureFace, outTexCoord), 0.2);
objectColor = vec4(1.0f, 0.5f, 0.31f, 1.0f);
vec4 ambient = getAmbientFactor();
vec4 diffuse = getDiffuseFactor();
vec4 specular = getSpecularFactor();
FragColor = (ambient + diffuse + specular) * objectColor;
}
比起上一节,又复杂了一些。首先,我们为材质定义了一个结构体Material,同时声明了一个material变量来描述物体的材质,它的四个成员变量和我们在MaterialCube类中get的是一一对应的。
struct Material {
vec3 ambient;
vec3 diffuse;
vec3 specular;
float shininess;
};
uniform Material material;
接着我们又定义了光源的结构体Light,同时也声明了变量light用来描述光源的三个分量,它的几个属性和材质的用法是完全一样了。
struct Light {
vec3 position;
vec3 ambient;
vec3 diffuse;
vec3 specular;
};
uniform Light light;
在getAmbientFactor、getDiffuseFactor、getSpecularFactor三个方法中,我们根据index位置的不同,来考虑不同的影响分量,最终绘制出来的也就是我们前面看到的最终效果了。看了这三个方法中的差别,大家很直接就能体会到,其实不管是材质,还是光源分量,在计算时,最终都是作用的颜色上,所以肯定是在片段着色器中起作用的,另外如果抛开概念,单纯对效果计算而言,根本不需要区分什么材质,什么光源分量,就是一个float或者vec变量而已,它们会参与计算,自然也就是影响最终的结果了,它们占的份量大,对结果的影响就越大,份量小,对结果的影响也就越小了。
本节的内容到这里就结束了,下一节我们继续学习光照贴图的内容。