向量的模:http://www.baike.com/wiki/%E5%90%91%E9%87%8F%E7%9A%84%E6%A8%A1
三维向量的模为 根号x^2 + y^2 + z^2
球面任意一点 到球心的距离都是半径R 所以x^2 + y^2 + z^2 = R^2
向量的运算:http://blog.csdn.net/he_wen_jian/article/details/25533829
求法向量:http://blog.csdn.net/lidec/article/details/51873647
opengles光照总结:http://blog.csdn.net/kesalin/article/details/8451595
散射光比环境光稍复杂,需要用到以上的一些向量知识
散射光的计算公式:
散射光照结果=材质的反射系数 x 环境光强度 x max(0, dot(N, L))
N为顶点法向量 L为光源向量
demo:
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.app.Activity;
import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.Bundle;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new MSur(this));
}
}
class MSur extends GLSurfaceView {
Render render;
public MSur(Context context) {
super(context);
this.setEGLContextClientVersion(2);
render = new Render(context);
setRenderer(render);
this.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
}
}
class Render implements GLSurfaceView.Renderer {
private static final float r=0.8f;
//TODO 1 设置光源位置
private static final float LIGHT[]={1.0f, 0.5f, 1.5f};
private int vCount;
private Context ctx;
private FloatBuffer fbVertex;
private FloatBuffer fbNormal;
private FloatBuffer fbLight;
static float[] mMMatrix = new float[16];
int mProgram;// 自定义渲染管线程序id
int muMVPMatrixHandle;// 总变换矩阵引用id
int maPositionHandle; // 顶点位置属性引用id
int maNormalHandle; //顶点法向量属性引用
int maLightLocationHandle;//光源位置属性引用
public static float[] mProjMatrix = new float[16];// 4x4矩阵 投影用
public static float[] mVMatrix = new float[16];// 摄像机位置朝向9参数矩阵
public static float[] mMVPMatrix;// 最后起作用的总变换矩阵
public Render(Context ctx) {
super();
this.ctx = ctx;
}
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glClearColor(0f, 0f, 0f, 1.0f);
initVertex();
initShader();
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
float ratio = (float) width / height;
Matrix.frustumM(mProjMatrix, 0, -ratio, ratio, -1, 1, 1, 10);
Matrix.setLookAtM(mVMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
draw();
}
public void draw() {
GLES20.glUseProgram(mProgram);
Matrix.setRotateM(mMMatrix, 0, 0, 0, 1, 0);
mMVPMatrix = new float[16];
Matrix.multiplyMM(mMVPMatrix, 0, mVMatrix, 0, mMMatrix, 0);
Matrix.multiplyMM(mMVPMatrix, 0, mProjMatrix, 0, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
//TODO 6 将光源位置传入着色器程序
GLES20.glUniform3fv(maLightLocationHandle, 1, fbLight);
GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, fbVertex);
//TODO 7 将顶点法向量数据传入渲染管线
GLES20.glVertexAttribPointer(maNormalHandle, 3, GLES20.GL_FLOAT, false, 3 * 4, fbNormal);
GLES20.glEnableVertexAttribArray(maPositionHandle);
//TODO 8 启用顶点法向量数据
GLES20.glEnableVertexAttribArray(maNormalHandle);
GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vCount);
}
//初始化数据
private void initVertex() {
ArrayList<Float> alVertix = new ArrayList<Float>();
final int angleSpan = 10;// 将球进行单位切分的角度
for (int vAngle = -90; vAngle < 90; vAngle = vAngle + angleSpan)// 垂直方向angleSpan度一份
{
for (int hAngle = 0; hAngle <= 360; hAngle = hAngle + angleSpan)// 水平方向angleSpan度一份
{
float x0 = (float) (r * Math.cos(Math.toRadians(vAngle)) * Math.cos(Math.toRadians(hAngle)));
float y0 = (float) (r * Math.cos(Math.toRadians(vAngle)) * Math.sin(Math.toRadians(hAngle)));
float z0 = (float) (r * Math.sin(Math .toRadians(vAngle)));
float x1 = (float) (r * Math.cos(Math.toRadians(vAngle)) * Math.cos(Math.toRadians(hAngle + angleSpan)));
float y1 = (float) (r * Math.cos(Math.toRadians(vAngle)) * Math.sin(Math.toRadians(hAngle + angleSpan)));
float z1 = (float) (r * Math.sin(Math.toRadians(vAngle)));
float x2 = (float) (r * Math.cos(Math.toRadians(vAngle + angleSpan)) * Math.cos(Math.toRadians(hAngle + angleSpan)));
float y2 = (float) (r * Math.cos(Math.toRadians(vAngle + angleSpan)) * Math.sin(Math.toRadians(hAngle + angleSpan)));
float z2 = (float) (r * Math.sin(Math.toRadians(vAngle + angleSpan)));
float x3 = (float) (r * Math.cos(Math.toRadians(vAngle + angleSpan)) * Math.cos(Math.toRadians(hAngle)));
float y3 = (float) (r * Math.cos(Math.toRadians(vAngle + angleSpan)) * Math.sin(Math.toRadians(hAngle)));
float z3 = (float) (r * Math.sin(Math.toRadians(vAngle + angleSpan)));
alVertix.add(x1);
alVertix.add(y1);
alVertix.add(z1);
alVertix.add(x3);
alVertix.add(y3);
alVertix.add(z3);
alVertix.add(x0);
alVertix.add(y0);
alVertix.add(z0);
alVertix.add(x1);
alVertix.add(y1);
alVertix.add(z1);
alVertix.add(x2);
alVertix.add(y2);
alVertix.add(z2);
alVertix.add(x3);
alVertix.add(y3);
alVertix.add(z3);
}
}
vCount = alVertix.size() / 3;
float vertices[]=new float[alVertix.size()];
for (int i=0;i<vertices.length;i++) {
vertices[i]=alVertix.get(i);
}
ByteBuffer bbv = ByteBuffer.allocateDirect(vertices.length * 4);
bbv.order(ByteOrder.nativeOrder());
fbVertex = bbv.asFloatBuffer();
fbVertex.put(vertices);
fbVertex.position(0);
//TODO 4 法向量
ByteBuffer bbn = ByteBuffer.allocateDirect(vertices.length * 4);
bbn.order(ByteOrder.nativeOrder());
fbNormal = bbn.asFloatBuffer();
fbNormal.put(vertices);
fbNormal.position(0);
//TODO 5 光源位置
ByteBuffer bbl = ByteBuffer.allocateDirect(LIGHT.length * 4);
bbl.order(ByteOrder.nativeOrder());
fbLight = bbl.asFloatBuffer();
fbLight.put(LIGHT);
fbLight.position(0);
}
//初始化shader
private void initShader() {
String vertex = loadSH("vertex.sh");
String shader = loadSH("frag.sh");
int verS = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
if (verS != 0) {
GLES20.glShaderSource(verS, vertex);
GLES20.glCompileShader(verS);
}
int fragS = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
if (fragS != 0) {
GLES20.glShaderSource(fragS, shader);
GLES20.glCompileShader(fragS);
}
mProgram = GLES20.glCreateProgram();
if (mProgram != 0) {
GLES20.glAttachShader(mProgram, verS);
GLES20.glAttachShader(mProgram, fragS);
GLES20.glLinkProgram(mProgram);
}
maPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
muMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
//TODO 2 获取程序中顶点法向量属性引用
maNormalHandle= GLES20.glGetAttribLocation(mProgram, "aNormal");
//TODO 3 获取程序中光源位置引用
maLightLocationHandle=GLES20.glGetUniformLocation(mProgram, "uLightLocation");
}
//将sh文件加载进来
private String loadSH(String fname) {
String result = null;
try {
InputStream in = ctx.getAssets().open(fname);
int ch = 0;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
while ((ch = in.read()) != -1) {
baos.write(ch);
}
byte[] buff = baos.toByteArray();
baos.close();
in.close();
result = new String(buff, "UTF-8");
result = result.replaceAll("\\r\\n", "\n");
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}
vertex.sh
uniform mat4 uMVPMatrix; //总变换矩阵
uniform vec3 uLightLocation; //光源位置
attribute vec3 aPosition; //顶点位置
attribute vec3 aNormal; //顶点法向量
varying vec4 vDiffuse; //用于传递给片元着色器的散射光分量
void main()
{
gl_Position = uMVPMatrix * vec4(aPosition,1);
vec4 vAmbient = vec4(0.9, 0.9, 0.9, 1.0); //设置环境光强度
vec3 normalTarget=aPosition+aNormal;
vec3 newNormal=(vec4(normalTarget,1)).xyz-(vec4(aPosition,1)).xyz; //求出法向量
newNormal=normalize(newNormal); //向量规格化
vec3 vp= normalize(uLightLocation-(vec4(aPosition,1)).xyz); //计算从表面点到光源位置的向量
vp=normalize(vp); //向量规格化
float nDotViewPosition=max(0.0, dot(newNormal,vp)); //求法向量与vp向量的点积与0的最大值
vDiffuse=vAmbient*nDotViewPosition; //计算散射光的最终强度
}
frag.sh
precision mediump float;
varying vec4 vDiffuse; //用于传递给片元着色器的散射光分量
void main()
{
vec4 vFinalColor = vec4(1.0, 0.0, 1.0, 0.0);
gl_FragColor = vFinalColor * vDiffuse; //通过散射光分量获得最终颜色
}
效果: