OpenGL光照模型,在固定管线中,主要是调用OpenGL函数实现,如果使用着色器,该怎么实现。本文的例子是移植OpenGL 4.0 Shading Language Cookbook中第二章的例子。代码已经移植到Android上。

散射光计算主要涉及到两个向量,第一个是顶点到光源的向量S,以及顶点处的法向量N。光照计算在眼睛坐标中进行。具体见下图所示:

有这两个向量之后,还要考虑顶点处的漫反射系数以及光源强度,最终顶点处的光照强度的结果可以通过下列公式计算:

Ld为光源强度,Kd为漫反射系数。关于该公式的推导什么的,这里不做过多的描述。

有了上面的基本原理之后,下面我们就可以来一步步构建我们的demo了。

第一步:编写顶点和片段着色器

1、顶点shader

#version 310 esprecision mediump float;layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexNormal;out vec3 LightIntensity;uniform vec4 LightPosition; // 光源位置(眼睛坐标)
uniform vec3 Kd;            // 漫反射系数
uniform vec3 Ld;            // 漫反射光强度uniform mat4 ModelViewMatrix;
uniform mat3 NormalMatrix;
uniform mat4 ProjectionMatrix;
uniform mat4 MVP;       //直接传入MVP矩阵是为了减少每个顶点的计算量void main()
{vec3 tnorm = normalize( NormalMatrix * VertexNormal);     //法线转换到眼睛坐标vec4 eyeCoords = ModelViewMatrix * vec4(VertexPosition,1.0);vec3 s = normalize(vec3(LightPosition - eyeCoords));LightIntensity = Ld * Kd * max( dot( s, tnorm ), 0.0 );       //漫反射光计算gl_Position = MVP * vec4(VertexPosition,1.0);
}

2、片段shader

#version 310 esprecision mediump float;in vec3 LightIntensity;layout( location = 0 ) out vec4 FragColor;void main() {FragColor = vec4(LightIntensity, 1.0);
}

第二步:渲染矿建搭建

渲染框架也就是一个架子,只有先把这个弄好了之后才能做最后的渲染工作。

1、在Java层用GLSurfaceView结合Render进行渲染,关于这方面的资料网上有很多。

这个弄好了之后,还需要通过NDK调用到C++层执行实际的渲染。这里可以弄一个接口类,这个类专门负责native函数实现。

import javax.microedition.khronos.egl.EGLConfig;
import android.content.res.AssetManager;public class GLinterface {public native static void onDrawFrame();public native static void onSurfaceChanged(int width, int height);public native static void onSurfaceCreated(EGLConfig config);public native static void initializeAssetManager(AssetManager assetManager);}

然后Render类中调用它即可。

public class GlRenderer implements GLSurfaceView.Renderer{@Overridepublic void onDrawFrame(GL10 gl) {// TODO Auto-generated method stubGLinterface.onDrawFrame();}@Overridepublic void onSurfaceChanged(GL10 gl, int width, int height) {// TODO Auto-generated method stubGLinterface.onSurfaceChanged(width, height);}@Overridepublic void onSurfaceCreated(GL10 gl, EGLConfig config) {// TODO Auto-generated method stubGLinterface.onSurfaceCreated(config);}
}

initializeAssetManager主要负责向C层传递AssetManager的对象,然后C层读取assets中的文件,这里我主要是读取shader文件。

initializeAssetManager在Activity的oncreate中调用。

2、C层框架的搭建:

java层的native函数声明完之后,然后可以生成对应的C函数声明,这里主要是初始化,窗口变化以及渲染这三个函数,这些都弄好之后,然后可以构建这个例子需要用到的场景了,场景中就包括了对渲染数据的准备,shader的加载编译,向shader传数据。

场景是由一个类来管理,类的声明如下:

class VBOTorus;
class DiffuseShader
{
public:DiffuseShader();virtual ~DiffuseShader();void Init();void Resize(int width,int height);void Draw();private:GLuint vaoHandle;GLSLProgram mProgram;Matrix4x4 model;Matrix4x4 view;Matrix4x4 projection;VBOTorus *torus;void setMatrices();
};

(1)构造函数以及shader加载

shader加载用Assetsmanager来读取数据,具体为,

extern std::string strVert;
extern std::string strFrag;void GetShaderFile(AAssetManager* mgr,const char* vertFile,const char* fragFile)
{//打开顶点shaderAAsset* asset = AAssetManager_open(mgr,vertFile,AASSET_MODE_UNKNOWN);off_t lenght = AAsset_getLength(asset);__android_log_print(ANDROID_LOG_INFO,"GLES2","length = %ld",lenght);strVert.resize(lenght+1,0);memcpy((char*)strVert.data(),AAsset_getBuffer(asset),lenght);__android_log_print(ANDROID_LOG_INFO,"GLES2","content = %s",strVert.c_str());AAsset_close(asset);asset = AAssetManager_open(mgr,fragFile,AASSET_MODE_UNKNOWN);lenght = AAsset_getLength(asset);__android_log_print(ANDROID_LOG_INFO,"GLES2","length = %ld",lenght);strFrag.resize(lenght+1,0);memcpy((char*)strFrag.data(),AAsset_getBuffer(asset),lenght);__android_log_print(ANDROID_LOG_INFO,"GLES2","content = %s",strVert.c_str());AAsset_close(asset);
}
JNIEXPORT void JNICALL Java_com_example_ndkgles_GLinterface_initializeAssetManager(JNIEnv *env, jclass clsObj, jobject assetManager)
{AAssetManager* mgr = AAssetManager_fromJava(env, assetManager);//GetShaderFile(mgr,"uniformBlock.vsh","uniformBlock.fsh");GetShaderFile(mgr,"diffuse.vert","diffuse.frag");
}

Java_com_example_ndkgles_GLinterface_initializeAssetManager对应的函数就是Java层的initializeAssetManager函数,这样shader的文本已经加载进来了,然后剩下shader创建。程序对象编译了。

extern std::string strVert;
extern std::string strFrag;DiffuseShader::DiffuseShader()
{// TODO Auto-generated constructor stubmProgram.InitWithShader(strVert.c_str(), strFrag.c_str());mProgram.LinkProgram();
}

mProgram的类型是GLSLProgram,这个可以参考我的前一篇博客, GLSL程序对象封装。strVert和strFrag是全局变量,用来存放shader的文本内容。

2、init函数

init函数主要是向shader传递数据以及创建渲染的对象

glClearColor(0.0,0.0,0.0,1.0);glEnable(GL_DEPTH_TEST);torus = new VBOTorus(0.7f, 0.3f, 30, 30);Matrix4x4::CreateRotationX(-35.0f,model);Matrix4x4 modelTemp;Matrix4x4::CreateRotationY(35.0f,modelTemp);model *= modelTemp;Matrix4x4::CreateScale(1.0,1.0,1.0,modelTemp);model *= modelTemp;Matrix4x4::CreateLookAt(Vector3(0.0f,0.0f,2.6f),Vector3(0.0f,0.0f,0.0f),Vector3(0.0f,1.0f,0.0f),view);projection = Matrix4x4::IDENTITY;mProgram.SetUniformVariable("Kd", 0.9f, 0.5f, 0.3f);mProgram.SetUniformVariable("Ld", 1.0f, 1.0f, 1.0f);//设置灯光位置Vector4 vec4(0,0,0,0);vec4 = view * Vector4(5.0f,5.0f,2.0f,1.0f);mProgram.SetUniformVariable("LightPosition", vec4.x,vec4.y,vec4.z,vec4.w );

关于上面的矩阵操作,可以用NDK自带的,或者你自己写也可以,这个是我的一个个人项目,现在还不适合公开。

3、resize函数

glViewport(0,0,width,height);Matrix4x4::CreatePerspective(70.0f,(Real)width/height,0.3f,100.f,projection);

4、draw函数

这个函数就是最后的渲染函数了,主要工作就是设置MVP矩阵、法线矩阵等操作。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);Matrix4x4 mv = view * model;mProgram.SetUniformMatrix4f("ModelViewMatrix", 1, true, &mv[0][0]);Matrix3x3 matNormal = mv.GetMatrix3().Inverse().Transpose();mProgram.SetUniformMatrix3f("NormalMatrix",1, true, &matNormal[0][0]);mProgram.SetUniformMatrix4f("MVP", 1, true, &(projection * mv)[0][0]);torus->render();

VBOTorus这个类是该demo显示的对象,代码我已经上传, 下载地址,在这里就不啰嗦了。

第三步、连接C和Java之间的桥梁

经过前两部,Java和C之间的工作都做完,并且接口已经定义好,这时只需要在native的实现函数里面调用相应的功能即可。

DiffuseShader* pBasicShder = NULL;/** Class:     com_example_ndkgles_GLinterface* Method:    onDrawFrame* Signature: ()V*/
JNIEXPORT void JNICALL Java_com_example_ndkgles_GLinterface_onDrawFrame(JNIEnv *env, jclass clsObj)
{//OnDrawGlFrame();pBasicShder->Draw();
}/** Class:     com_example_ndkgles_GLinterface* Method:    onSurfaceChanged* Signature: (II)V*/
JNIEXPORT void JNICALL Java_com_example_ndkgles_GLinterface_onSurfaceChanged(JNIEnv *env, jclass clsObj, jint width, jint height)
{//OnResize(width,height);pBasicShder->Resize(width, height);
}/** Class:     com_example_ndkgles_GLinterface* Method:    onSurfaceCreated* Signature: (Ljavax/microedition/khronos/egl/EGLConfig;)V*/
JNIEXPORT void JNICALL Java_com_example_ndkgles_GLinterface_onSurfaceCreated(JNIEnv *env, jclass clsObj, jobject obj)
{//OnInited();pBasicShder = new DiffuseShader();pBasicShder->Init();
}

上面这几个函数就对应着Java层的GLinterface,至此,所有的工作都做完了,连接手机,不出意外,图像显示出来了。

以前刚开始弄shader的时候感觉很麻烦,现在我觉得只要把一些东西封装好能复用也没想象的那么繁琐。

OpenGL ES着色语言-光照效果之散射光相关推荐

  1. OpenGL ES着色器语言之变量和数据类型

    所有变量和函数在使用前必须声明.变量和函数名是标识符. 没有默认类型,所有变量和函数声明必须包含一个声明类型以及可选的修饰符.变量在声明的时候首先要标明类型,后边可以跟多个变量,之间用逗号隔开.很多情 ...

  2. OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)...

    OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-wh ...

  3. OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章)

    OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章) 4.5精度和精度修饰符 4.5.1范围和精度 用于存储和展示浮点数.整数变量的范围和精度依赖于数值的源(varying,unifo ...

  4. OpenGL ES着色器语言(GLSL ES)规范 ——下篇

    文章目录 前言 分支和循环 if.if-else for continue.break.discard 着色器内置变量 函数 函数定义 规范声明 webgl内置函数 存储限定字 const attri ...

  5. Modern OpenGL - GLSL着色语言2:GLSL入口函数和GLSL中的变量

    文章目录 1 GLSL的入口函数和基本结构 2 GLSL中的变量声明 3 GLSL中变量的作用域 4 GLSL中变量的初始化 5 GLSL中变量的隐式转换 1 GLSL的入口函数和基本结构 对于很多编 ...

  6. Modern OpenGL - GLSL着色语言3:GLSL中的数据类型

    文章目录 1 GLSL的基本数据类型 2 GLSL的聚合类型:向量和矩阵 2.1 向量 2.1.1 向量初始化 2.1.1.1 向量初始化 2.1.1.2 向量构造函数的截短 2.1.1.3 向量构造 ...

  7. iOS OpenGl ES着色器

    首先创建两个文件分别为simple.frag和simple.vert simple.frag内容 const char* SimpleFragmentShader = STRINGIFY(varyin ...

  8. OpenGL ES SDK for Android - 3

    Bloom 使用OpenGL ES 3.0的绽放效果. 绽放效果:强度从非常弱变为非常强烈. 该应用程序显示了bloom实现. 它绘制了以二维5x5阵列排列的立方体,只有对角线的阵列才绽放. 绽放效果 ...

  9. iPhone的OpenGL ES的资源- 18,你必须知道 !

    iPhone的OpenGL ES的资源- 18,你必须知道 ! OpenGL ES是编程的iPhone使用的图形API. 如果要创建自己的游戏引擎,从头开始设计一个游戏,或者创造一些其他图形密集型应用 ...

最新文章

  1. 自己实现简单的AOP(三) 实现增强四项基本功能
  2. 张勇云栖大会谈科技担当与责任:做开放共享人人受益的好科技
  3. 本田da屏怎么进wince系统_谁说思域要停产的?打脸来得不是一般的快 第十一代本田思域Prototype...
  4. 通过调用外部exe的方法实现c#调用java
  5. MySQL数据表查询操作
  6. CentOS 7 忘记root密码的解决之道
  7. 【自然语言处理系列】预训练模型原理和实践综述 | 附汇报PPT原稿和18篇论文
  8. html静态网页制作代码
  9. 中国石油大学-《现代应用文写作》第三阶段在线作业
  10. 伽马校正(Gamma Correction)与sRGB
  11. c++课程设计之通讯录电话簿管理设计
  12. openstack源码架构_openstack创建虚拟机源码阅读
  13. 关于企业微信二次开发
  14. 英语句式参考纯享版 - 主语从句 - 表语从句
  15. 基于Salt Event系统构建Master端returner
  16. linux上系统烧录工具,烧录操作系统安装光盘镜像到 U 盘的方法
  17. vue获取地址栏参数
  18. 看黑客如何一步步攻破你的服务器
  19. CSS 实现切角效果
  20. CC++编程环境搭建-四种常用开发工具(VSCode,Dev,CodeBlocks,Clion)

热门文章

  1. 【第62期】学会数据分析,抢占职场风口机遇
  2. Faster-RCNN实现遥感图像滑坡识别
  3. 算法小抄9-快慢指针
  4. [open source]点阵字体产生器发布
  5. [项目管理-22]:项目中开环、闭环、安全、监控四种沟通模型:UDP/TCP/SCTP/PID模型
  6. 研究c语言必须的软件,为什么必须学习C语言
  7. 深度学习100例-循环神经网络(RNN)心脏病预测 | 第46天
  8. 看云笔记和有道云笔记哪个好用,看谁更胜一筹
  9. pypi 清华源镜像使用方法
  10. linux系统组成及结构