openGL光照:
光的成分:由RGBA值决定
1.环境光:没有方向,向四周均匀发散,全局因素,ambient
2.散射光:有方向性,根据入射光角度均匀反射开来,物体表面的颜色主要取决于散射光,diffuse
3.镜面光:具有很强的方向性,高度方向性,specular
没有一种光源是纯粹由上面所述的任何一种类型的光所组成的。事实上,光源是由各种强度的不同类型的光所组成的。例如,实验室中的红色激光束几乎是由纯的红色镜面光所组成的,当它照射物体时,将会产生一个非常强的亮点。但是,烟或灰尘将会导致激光束有所发散,产生非常小的环境光成分,使房间中的其他物体染上一层微微的红色。如果光束极为耀眼地照射到一个表面上,将会产生非常小的散射成分,可以在它所照亮的表面上看到(尽管在很大程度上会被镜面亮点所掩盖)。
因此,场景中的一-种光源是由3种光成分所组成的:环境光、散射光和镜面光。就像颜色的成分一样,每种光成分是用一个RGBA值定义的,它描述了组成这种成分的红光、绿光和蓝光的强度(对于光的颜色而言,alpha值被忽略)。例如,红色激光可以用下表所列出的成分值来描述。
注意,红色激光束不存在绿色或蓝色光。另外,注意镜面光、散射光和环境光的各种强度范围都是从0.0到1.0。读者可以把这张表解释为红色激光束在某些场景中具有非常多的镜面光成分、比较少的散射光成分和极其微少的环境光成分。当它发出光时,我们很可能会看到一个红色的亮点。另外,由于房间内的条件(烟、尘等)所致,激光中的环境光成分( 由于房间中的烟和灰尘所致)会把极其微量的激光散布到整个房间里。
现实材料
光是颜色方程式的一部分。物体本身有自己的颜色。物体的颜色由它所反射的光的波长决定的。篮球反射绝大多部分蓝色光子,吸收绝大部分其他光子。照射篮球的光具有蓝色的光子,并且能够看到反射光。现实世界中,绝大多数场景都是有包含了所有颜色光的白光照射的,因此,在白光照射下,所有物体都呈现出自然地颜色。如果蓝球放在黑色房间里,使用黄光照射,看到的是黑色的,因为所有黄光都被吸收,并且没有蓝光被反射。
材料的属性
绘制物体时,OpenGL会决定物体的每个像素使用何种颜色。物体具有反射的颜色,光源有自己的颜色,如何判定该使用哪种颜色呢?
图元的每个顶点分配一个RGB值,它是环境,散射和镜面光乘以材料属性的环境,散射镜面光反射率产生的净效果。由于使用了平滑着色,因此实现了照明的幻觉。
计算环境光效果
环境光源rgb值各为一般(0.5,0.5,0.5),如果该环境光所照射的物体的反射属性按照RGB术语指定为(0.5,1.0,0.5),则环境光产生的净成分是() (0.5 * 0.5, 0.5 * 1.0, 0.5 * 0.5) = (0.25, 0.5, 0.25)。材料的颜色成分实际上绝对了入射光的反射比例。
散射和镜面光效果
散射光也有RGB值,他们也以相同的方式和材料的反射属性进行交互。但是,散射光具有方向性,并且物体表面上光的强度取决于表面和光源的角度,表面和光源的距离以及所有相关联的衰减因素(光源和物体间是否有雾)等。镜面光也是如此。最后,所有全部的RGB值加在一起,产生物体的最终颜色。如果任何一种颜色成分之大于1.0,就被截断。一般,光源和材料的环境和散射成分相同,在决定物体的颜色方面具有最显著的效果。镜面光和材料的镜面反射倾向于淡灰色或白色。镜面光成分受入射角度影响非常大,物体表面受到镜面光照之处通常是白色的。
向场景添加光照
启用光照gl.glEnable(GL_LIGHTING);
该调用告诉OpenGL在确定场景中每个顶点的颜色使用材料属性和光照参数。但是如果没有指定任何材料属性和光照参数,物体将会保持为黑暗的无光照状态。如图所示。
注:光照启用后,顶点颜色将被忽略,需要使用法线数组才能进行颜色设置。
设置宇宙背景发射光
OpenGL提供了一个全局光源,他只发射环境光,他向所有方向均匀发射发散的光源。他的作用是可以照明没有被光源照射的物体的背面。如果光照场景太暗,可以使用它调节全局环境光。
全局环境光在OpenGL中的光照模型中设置的,可以用glLightModel函数进行修改。
GLfloat ambientLight[] = { 1.0f, 1.0f, 1.0f, 1.0f }; //白光
glEnable(GL_LIGHTING); //启用光照
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight); //设置全局环境光
全局环境光的默认值是(0.2, 0.2, 0.2, 1.0),是非常暗淡的。
设置材料属性
两种方式设置材料,第一种是在指定每个多边形之前使用glMaterial函数.
Glfloat gray[] = { 0.75f, 0.75f, 0.75f, 1.0f };
glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, gray);
第一个参数指定了作用在正面(GL_FRONT),背面(GL_BACK)还是双面(GL_FRONT_BACK)。
第二个参数指定设置哪个属性。
第三个是参数值.本例中环境光和散射光相同,而且这也适合于大多数情况。
如果在每组多边形钱都调用该方法任务相当枯燥。可以使用第二种方式——颜色追踪。
颜色追踪
是我们推荐的设置材料属性的方法。可以告诉OpenGL仅仅通过glColor来设置材料属性。为了启用颜色追踪,需
要如下调用:
glEnable(GL_COLOR_MATERIAL);
接着glColorMaterial函数根据glColor设置的颜色值来指定材料参数。
比如,设置环境光和散射光属性。
gl.glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
节省了大量代码,随着不同多边形颜色数量增多,执行速度将会更快。
glEnable(GL_COLOR_MATERIAL);
glColorMaterial(GL_FRONT,GL_AMBIENT_AND_DIFFUSE);
glcolor3f(0.75f, 0.75f, 0.75f);
public class MyActivity extends Activity {
private MyRenderer render;
private MyGLSurfaceView view;
// 光照
private boolean enableLighting = false;
// 全局环境光
private float global$ambient$r = 0.3f;
private float global$ambient$g = 0.3f;
private float global$ambient$b = 0.3f;
private float global$ambient$a = 1f;
// 材料的环境光和散射光的反射率
private float material$ambientanddiffuse$r = 0f;
private float material$ambientanddiffuse$g = 0f;
private float material$ambientanddiffuse$b = 0f;
private float material$ambientanddiffuse$a = 1f;//
// 是否启用颜色追踪
private boolean enableColorMatrial = false;
private float color$r = 0.8f;
private float color$g = 0.8f;
private float color$b = 0.8f;
private float color$a = 1f;
// 是否启用光源0
private boolean enableLight0 = false;
// 光源0-环境光
private float light0$ambient$r = 0f;
private float light0$ambient$g = 0f;
private float light0$ambient$b = 0f;
private float light0$ambient$a = 1f;
// 光源0-散射光
private float light0$diffuse$r = 0f;
private float light0$diffuse$g = 0f;
private float light0$diffuse$b = 0f;
private float light0$diffuse$a = 1f;
// 光源0-位置
private float light0$pos$x = 0;
private float light0$pos$y = 1f;
private float light0$pos$z = 10f;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
view = new MyGLSurfaceView(this);
render = new MyRenderer();
view.setEGLConfigChooser(5, 6, 5, 0, 8, 8);
view.setRenderer(render);
view.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);// 脏渲染,命令渲染
setContentView(view);
}
class MyGLSurfaceView extends GLSurfaceView {
public MyGLSurfaceView(Context context) {
super(context);
}
public MyGLSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
}
class MyRenderer implements GLSurfaceView.Renderer {
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0f, 0f, 0f, 1f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnable(GL10.GL_DEPTH_TEST);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
float ratio = (float) width / (float) height;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-ratio, ratio, -1, 1, 3f, 7f);
}
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glColor4f(0f, 0f, 1f, 1f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, 5, 0, 0, 0, 0, 1, 0);
// 单调模式
gl.glShadeModel(GL10.GL_FLAT);
/******************** 光照 *******************************/
// 启用光照
if (enableLighting)
gl.glEnable(GL10.GL_LIGHTING);
else
gl.glDisable(GL10.GL_LIGHTING);
// 设置全局环境光
float[] global_ambient = { global$ambient$r, global$ambient$g, global$ambient$b, global$ambient$a, };
gl.glLightModelfv(GL10.GL_LIGHT_MODEL_AMBIENT, arr2FloatBuffer(global_ambient));
// 1.设置材料的反射率(环境光和散射光)
float[] matrial_ambient_diffuse = { material$ambientanddiffuse$r, material$ambientanddiffuse$g,
material$ambientanddiffuse$b, material$ambientanddiffuse$a, };
gl.glLightfv(GL10.GL_FRONT_AND_BACK, GL10.GL_AMBIENT_AND_DIFFUSE, arr2FloatBuffer(matrial_ambient_diffuse));
// 颜色追踪 glColor();
if (enableColorMatrial)
gl.glEnable(GL10.GL_COLOR_MATERIAL);
else
gl.glDisable(GL10.GL_COLOR_MATERIAL);
// openGLES是固定的将环境光合散射光最终颜色
// openGL:glColorMatrial();
gl.glColor4f(color$r, color$g, color$b, color$a);
// 光源0
if (enableLight0)
gl.glEnable(GL10.GL_LIGHT0);
else
gl.glDisable(GL10.GL_LIGHT0);
// 环境光
float[] light0_ambient = { light0$ambient$r, light0$ambient$g, light0$ambient$b, light0$ambient$a, };
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_AMBIENT, arr2FloatBuffer(light0_ambient));
// 散射光
float[] light0_diffuse = { light0$diffuse$r, light0$diffuse$g, light0$diffuse$b, light0$diffuse$a, };
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_DIFFUSE, arr2FloatBuffer(light0_diffuse));
// 光源 0-位置,1.0表示光源就在此地
float[] light0pos = { light0$pos$x, light0$pos$y, light0$pos$z, 1.0f };
gl.glLightfv(GL10.GL_LIGHT0, GL10.GL_POSITION, arr2FloatBuffer(light0pos));
drawSphere(gl, 0.6f, 8, 8);// 绘制球体
}
}
private void drawSphere(GL10 gl, float r, int stack, int slice) {
// 计算球体坐标
float stackStep = (float) (Math.PI / stack);// 单位角度值
float sliceStep = (float) Math.PI / slice;// 水平圆递增的角度
float r0, r1, y0, y1, x0, x1, z0, z1;
float alpha0 = 0, alpha1 = 0;
float beta = 0;
List coordsList = new ArrayList();
// 外层循环
for (int i = 0; i < stack; i++) {
alpha0 = (float) (-Math.PI / 2 + (i * stackStep));
alpha1 = (float) (-Math.PI / 2 + ((i + 1) * stackStep));
y0 = (float) (r * Math.sin(alpha0));
r0 = (float) (r * Math.cos(alpha0));
y1 = (float) (r * Math.sin(alpha1));
r1 = (float) (r * Math.cos(alpha1));
// 循环每一层圆
for (int j = 0; j <= (slice * 2); j++) {
beta = j * sliceStep;
x0 = (float) (r0 * Math.cos(beta));
z0 = -(float) (r0 * Math.sin(beta));
x1 = (float) (r1 * Math.cos(beta));
z1 = -(float) (r1 * Math.sin(beta));
coordsList.add(x0);
coordsList.add(y0);
coordsList.add(z0);
coordsList.add(x1);
coordsList.add(y1);
coordsList.add(z1);
}
}
FloatBuffer fbb = list2FloatBuffer(coordsList);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, fbb);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, coordsList.size() / 3);
}
private FloatBuffer arr2FloatBuffer(float[] coords) {
ByteBuffer ibb = ByteBuffer.allocateDirect(coords.length * 4);
ibb.order(ByteOrder.nativeOrder());
FloatBuffer fbb = ibb.asFloatBuffer();
fbb.put(coords);
fbb.position(0);
return fbb;
}
private FloatBuffer list2FloatBuffer(List list) {
ByteBuffer ibb = ByteBuffer.allocateDirect(list.size() * 4);
ibb.order(ByteOrder.nativeOrder());
FloatBuffer fbb = ibb.asFloatBuffer();
for (Float f : list) {
fbb.put(f);
}
fbb.position(0);
return fbb;
}
}