android opengles光照效果-散射光

向量的模: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;  //通过散射光分量获得最终颜色
}

效果:
这里写图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值