灯光的测试例子:光源参数可以调节的测试场景
先看一下测试场景和效果。
场景中可以切换视图, 以方便观察三维体和灯光的位置。环境光,漫射光,镜面反射光都可以在四种颜色间切换。
灯光位置和摄像机位置(LookAt)可以输入数值或者点动调节,也可以按键盘的QEWASD六个键进行调节。
你还会注意到:球体对光的效果要敏感柔和些,而那个六面体BOX看来效果不好。这是因为灯光对顶点发生作用。在程序里面,球休的顶点数量有20*10,而BOX只有4*6个,而且还重合了一些顶点。
这一点,在3dsmax的全局光照里面表现很明显,做为墙壁的box顶点数量越大,计算出来光照效果越好。
勘误:box在创建的时候没有指定法线,这个也是重要原因,请参考例子SharpGL学习笔记(十五) 纹理映射 ,那里的box指定了法线,效果就很好了。(2016/9/4)
还有,界面上灯光位置是 -1,5,1,1 前三个是x,y,z, 后面的一个1不是坐标,它取值0或者1,表示灯光是定向光源(directonal),还是定位光源(positional)。
代码如下:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using SharpGL; namespace SharpGLWinformsApplication1
{ public partial class SharpGLForm : Form
{
private float rotation = 0.0f;
private bool isRotate = false;
private bool isLines = false;
private bool isFrontView = false;
private bool isLeftView = false;
private bool isTopView = false;
private bool isPerspective = true;
private float[] lightPos = new float[] { -, -, , };
private float[] lightSphereColor = new float[] { 1f, 1f, 1f };
private IList<float[]> lightColor = new List<float[]>();
private double[] lookatValue = { , , , , , , , , };
private IList<double[]> viewDefaultPos = new List<double[]>();
public SharpGLForm()
{
InitializeComponent();
} private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e)
{
OpenGL gl = openGLControl.OpenGL;
gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT);
gl.LoadIdentity();
gl.Rotate(rotation, 0.0f, 1.0f, 0.0f); drawGrid(gl);
DrawCube(ref gl, 1.5f,-1f, -2f, isLines);
drawOneSphere(ref gl,-,-,-,isLines);
if (isRotate)
rotation += 3.0f;
} private void setLightColor(OpenGL gl)
{
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightColor[]);
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, lightColor[]);
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, lightColor[]);
} private void openGLControl_OpenGLInitialized(object sender, EventArgs e)
{
OpenGL gl = openGLControl.OpenGL; //四个视图的缺省位置
viewDefaultPos.Add(new double[] { , , , , , , , , }); //透视
viewDefaultPos.Add(new double[] { , , , , , , , , }); //前视
viewDefaultPos.Add(new double[] { , , , , , , , , }); //左视
viewDefaultPos.Add(new double[] { , , , -, , , , , }); //顶视
lookatValue =(double[])viewDefaultPos[].Clone(); lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //环境光(ambient light)
lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //漫射光(diffuse light)
lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //镜面反射光(specular light) setLightColor(gl);
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos); gl.Enable(OpenGL.GL_LIGHTING);
gl.Enable(OpenGL.GL_LIGHT0); gl.ClearColor(, , , );
} private void drawOneSphere(ref OpenGL gl, float xPos, float yPos, float zPos, bool isLine)
{
gl.PushMatrix();
{
gl.Translate(xPos, yPos, zPos);
gl.Color(lightSphereColor);
drawSphere(gl,,,,isLine);
}
gl.PopMatrix();
} private void openGLControl_Resized(object sender, EventArgs e)
{ OpenGL gl = openGLControl.OpenGL;
gl.MatrixMode(OpenGL.GL_PROJECTION);
gl.LoadIdentity();
gl.Perspective(40.0f, (double)Width / (double)Height, 0.01, 100.0); gl.LookAt(lookatValue[], lookatValue[], lookatValue[],
lookatValue[], lookatValue[], lookatValue[],
lookatValue[], lookatValue[], lookatValue[]); gl.MatrixMode(OpenGL.GL_MODELVIEW);
updateLabInfo();
} internal void DrawCube(ref OpenGL Gl, float xPos, float yPos, float zPos, bool isLine)
{
Gl.PushMatrix();
Gl.Translate(xPos, yPos, zPos);
if (isLine)
Gl.Begin(OpenGL.GL_LINE_STRIP);
else
Gl.Begin(OpenGL.GL_POLYGON); /** 顶面 */
Gl.Vertex(0.0f, 0.0f, 0.0f);
Gl.Vertex(0.0f, 0.0f, -1.0f);
Gl.Vertex(-1.0f, 0.0f, -1.0f);
Gl.Vertex(-1.0f, 0.0f, 0.0f); /** 前面 */
Gl.Vertex(0.0f, 0.0f, 0.0f);
Gl.Vertex(-1.0f, 0.0f, 0.0f);
Gl.Vertex(-1.0f, -1.0f, 0.0f);
Gl.Vertex(0.0f, -1.0f, 0.0f); /** 右面 */
Gl.Vertex(0.0f, 0.0f, 0.0f);
Gl.Vertex(0.0f, -1.0f, 0.0f);
Gl.Vertex(0.0f, -1.0f, -1.0f);
Gl.Vertex(0.0f, 0.0f, -1.0f); /** 左面*/
Gl.Vertex(-1.0f, 0.0f, 0.0f);
Gl.Vertex(-1.0f, 0.0f, -1.0f);
Gl.Vertex(-1.0f, -1.0f, -1.0f);
Gl.Vertex(-1.0f, -1.0f, 0.0f); /** 底面 */
Gl.Vertex(0.0f, 0.0f, 0.0f);
Gl.Vertex(0.0f, -1.0f, -1.0f);
Gl.Vertex(-1.0f, -1.0f, -1.0f);
Gl.Vertex(-1.0f, -1.0f, 0.0f); /** 后面 */
Gl.Vertex(0.0f, 0.0f, 0.0f);
Gl.Vertex(-1.0f, 0.0f, -1.0f);
Gl.Vertex(-1.0f, -1.0f, -1.0f);
Gl.Vertex(0.0f, -1.0f, -1.0f);
Gl.End();
Gl.PopMatrix();
} void drawGrid(OpenGL gl)
{
//绘制过程
gl.PushAttrib(OpenGL.GL_CURRENT_BIT); //保存当前属性
gl.PushMatrix(); //压入堆栈
gl.Translate(0f, -2f, 0f);
gl.Color(0f, 0f, 1f); //在X,Z平面上绘制网格
for (float i = -; i <= ; i += )
{
//绘制线
gl.Begin(OpenGL.GL_LINES);
{
if (i == )
gl.Color(0f, 1f, 0f);
else
gl.Color(0f, 0f, 1f); //X轴方向
gl.Vertex(-50f, 0f, i);
gl.Vertex(50f, 0f, i);
//Z轴方向
gl.Vertex(i, 0f, -50f);
gl.Vertex(i, 0f, 50f); }
gl.End();
}
gl.PopMatrix();
gl.PopAttrib();
} void drawSphere(OpenGL gl,double radius,int segx,int segy,bool isLines)
{
gl.PushMatrix();
gl.Translate(2f, 1f, 2f);
var sphere = gl.NewQuadric();
if (isLines)
gl.QuadricDrawStyle(sphere, OpenGL.GL_LINES);
else
gl.QuadricDrawStyle(sphere, OpenGL.GL_QUADS);
gl.QuadricNormals(sphere, OpenGL.GLU_SMOOTH);
gl.QuadricOrientation(sphere, (int)OpenGL.GLU_OUTSIDE);
gl.QuadricTexture(sphere, (int)OpenGL.GLU_FALSE);
gl.Sphere(sphere, radius, segx, segy);
gl.DeleteQuadric(sphere);
gl.PopMatrix();
} private void moveObject(int obj,string keyName)
{
//obj==0移动视图
switch (keyName)
{
case "btnQ":
if (obj == ) ++lookatValue[]; //y
else
++lightPos[];
break;
case "btnE":
if (obj == ) --lookatValue[];
else
--lightPos[];
break;
case "btnW":
if (obj == ) --lookatValue[]; //z
else
--lightPos[];
break;
case "btnS":
if (obj == ) ++lookatValue[];
else
++lightPos[];
break;
case "btnA":
if (obj == ) --lookatValue[]; //X
else
--lightPos[];
break;
case "btnD":
if (obj == ) ++lookatValue[];
else
++lightPos[];
break;
}
} private void rbPerspective_CheckedChanged(object sender, EventArgs e)
{
switch (((RadioButton)sender).Name)
{
case "rbPerspective":
isPerspective = !isPerspective;
isFrontView = false;
isTopView = false;
isLeftView = false;
break;
case "rbLeft":
isLeftView = !isLeftView;
isFrontView = false;
isPerspective = false;
isTopView = false;
break;
case "rbFront":
isFrontView = !isFrontView;
isTopView = false;
isPerspective = false;
isLeftView = false;
break;
case "rbTop":
isTopView = !isTopView;
isPerspective = false;
isLeftView = false;
isFrontView = false;
break;
default:
return;
}
setViewDefaultValue();
openGLControl_Resized(null, null);
} private void cbxRotate_CheckedChanged(object sender, EventArgs e)
{
var cbx=((CheckBox)sender);
switch (cbx.Name)
{
case "cbxRotate":
isRotate = cbx.Checked;
break;
case "cbxLines":
isLines = cbx.Checked;
break;
case "cbxLightOff":
if (!cbx.Checked)
this.openGLControl.OpenGL.Enable(OpenGL.GL_LIGHT0);
else
this.openGLControl.OpenGL.Disable(OpenGL.GL_LIGHT0);
break;
}
} private void SharpGLForm_Load(object sender, EventArgs e)
{
this.cbxLightType.SelectedIndex = ;
updateLabInfo();
} private void updateLabInfo()
{
tbLightPos.Text = string.Format("{0},{1},{2},{3}", lightPos[], lightPos[], lightPos[], lightPos[]);
tbLookAt.Text = string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8}", lookatValue[], lookatValue[], lookatValue[],
lookatValue[], lookatValue[], lookatValue[], lookatValue[], lookatValue[], lookatValue[]);
} private void rbWhite_CheckedChanged(object sender, EventArgs e)
{
var rad = ((RadioButton)sender);
var lightType = this.cbxLightType.SelectedIndex;
if (rad.Checked)
{
switch (rad.Name)
{
case "rbWhite":
lightColor[lightType][] = 1f;
lightColor[lightType][] = 1f;
lightColor[lightType][] = 1f;
lightColor[lightType][] = 1f;
break;
case "rbRed":
lightColor[lightType][] = 1f;
lightColor[lightType][] = 0f;
lightColor[lightType][] = 0f;
lightColor[lightType][] = 1f;
break;
case "rbGreen":
lightColor[lightType][] = 0f;
lightColor[lightType][] = 1f;
lightColor[lightType][] = 0f;
lightColor[lightType][] = 1f;
break;
case "rbBlue":
lightColor[lightType][] = 0f;
lightColor[lightType][] = 0f;
lightColor[lightType][] = 1f;
lightColor[lightType][] = 1f;
break;
}
setLightColor(openGLControl.OpenGL);
}
} private void cbxLightType_SelectedIndexChanged(object sender, EventArgs e)
{
var lightType = this.cbxLightType.SelectedIndex;
if (lightType >= )
judgeColor(lightColor[lightType]);
} private void judgeColor(float[] color)
{
if (color[] == 1f && color[] == 1f && color[] == 1f && color[] == 1f)
rbWhite.Checked = true;
else if (color[] == 1f && color[] == 0f && color[] == 0f && color[] == 1f)
rbRed.Checked = true;
else if (color[] == 0f && color[] == 1f && color[] == 0f && color[] == 1f)
rbGreen.Checked = true;
else if (color[] == 0f && color[] == 0f && color[] == 1f && color[] == 1f)
rbBlue.Checked = true;
} private void btnQ_Click(object sender, EventArgs e)
{
moveObject(radioButton1.Checked ? : ,((Button)sender).Name);
openGLControl_Resized(null, null);
openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos);
} private void setViewDefaultValue()
{
if (isPerspective)
{
lookatValue = (double[])viewDefaultPos[].Clone();
}
else if (isFrontView)
{
lookatValue = (double[])viewDefaultPos[].Clone();
}
else if (isLeftView)
{
lookatValue = (double[])viewDefaultPos[].Clone();
}
else if (isTopView)
{
lookatValue = (double[])viewDefaultPos[].Clone();
}
} private void btnDefaultPOS_Click(object sender, EventArgs e)
{
if (radioButton1.Checked)
{
setViewDefaultValue();
}
else
{
lightPos = new float[] { -, -, , };
openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos);
}
openGLControl_Resized(null, null);
} private void openGLControl_KeyDown(object sender, KeyEventArgs e)
{
string name = string.Empty;
switch (e.KeyCode)
{
case Keys.W:
name = "btnW";
break;
case Keys.A:
name = "btnA";
break;
case Keys.S:
name = "btnS";
break;
case Keys.D:
name = "btnD";
break;
case Keys.Q:
name = "btnQ";
break;
case Keys.E:
name = "btnE";
break;
}
moveObject(radioButton1.Checked ? : , name);
openGLControl_Resized(null, null);
} private void btnSetPos_Click(object sender, EventArgs e)
{
if (radioButton1.Checked)
{
double[] ary = tbLookAt.Text.Split(',').Select(s => Convert.ToDouble(s)).ToArray();
lookatValue = ary;
openGLControl_Resized(null, null);
}
else
{
float[] ary = tbLightPos.Text.Split(',').Select(s => Convert.ToSingle(s)).ToArray();
lightPos = ary;
openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, ary);
} } }
}
有关灯光的代码如下粗体显示部分:
private void setLightColor(OpenGL gl)
{
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightColor[]);
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, lightColor[]);
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, lightColor[]);
} private void openGLControl_OpenGLInitialized(object sender, EventArgs e)
{
OpenGL gl = openGLControl.OpenGL; //四个视图的缺省位置
viewDefaultPos.Add(new double[] { , , , , , , , , }); //透视
viewDefaultPos.Add(new double[] { , , , , , , , , }); //前视
viewDefaultPos.Add(new double[] { , , , , , , , , }); //左视
viewDefaultPos.Add(new double[] { , , , -, , , , , }); //顶视
lookatValue =(double[])viewDefaultPos[].Clone(); lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //环境光(ambient light)
lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //漫射光(diffuse light)
lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //镜面反射光(specular light) setLightColor(gl);
gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos); gl.Enable(OpenGL.GL_LIGHTING);
gl.Enable(OpenGL.GL_LIGHT0);
gl.ClearColor(, , , );
}
第3,4,5行是创建灯光三个部分 环境光,漫射光,镜面反射光。
第24行是设定灯光的位置。
第26,27是让灯光开,有效。
灯光代码确实比较简单,这段代码其它部分没什么好说的,笔者按界面功能直述其意,朋友们应该很容易懂。
唯一要关注下的是:视图和灯光的参数修改是如何实时生效的。
原创文章,出自"博客园, 猪悟能'S博客" : http://www.cnblogs.com/hackpig/