几种光源

OpenGL实现三种光源 衰减:随着光线传播距离的增长逐渐削减光的强度通常叫做衰减(Attenuation)用一个世界空间位置、一个方向和一个切光角(Cutoff Angle)来表示的。衰减公式:1 / (常数项 + 一次项*距离 + 二次项 + 距离的平方)位于环境中某个位置的光源,它只朝一个特定方向而不是所有方向照射光线。当一个光源处于很远的地方时,来自光源的每条光线就会近似于互相平行。因为所有的光线都是平行的,所以物体与光源的相对位置是不重要的。分散在场景中的点,会朝着所有方向发光,会随着距离逐渐衰减。 阅读详情

Learn Opengl学习笔记


定向光(Directional Light)

当一个光源很远的时候,来自光源的每条光线接近于平行。这看起来就像所有的光线来自于同一个方向,无论物体和观察者在哪儿。当一个光源被设置为无限远时,它被称为定向光(Directional Light),因为所有的光线都有着同一个方向;它会独立于光源的位置。

我们知道的定向光源的一个好例子是,太阳。来自于太阳的所有的光线都被定义为平行光:

因为所有的光线都是平行的,对于场景中的每个物体光的方向都保持一致,物体和光源的位置保持怎样的关系都无所谓。由于光的方向向量保持一致,光照计算会和场景中的其他物体相似。

我们可以通过定义一个光的方向向量,来模拟这样一个定向光,而不是使用光的位置向量。着色器计算保持大致相同的要求,这次我们直接使用光的方向向量来代替用 lightDir 向量和 position 向量的计算:

//定向光 
struct DirectionLight
{
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform DirectionLight light;
...
void main()
{
    vec3 lightDir = normalize(-light.direction);
    ...
}

注意,我们首先对light.direction向量取反。目前我们使用的光照计算需要光的方向作为一个来自片段朝向的光源的方向,但是人们通常更习惯定义一个定向光作为一个全局方向,它从光源发出。所以我们必须对全局光的方向向量取反来改变它的方向;它现在是一个方向向量指向光源。同时,确保对向量进行标准化处理。

作为结果的lightDir向量被使用在diffuse和specular计算之前。

为了清晰地强调一个定向光对所有物体都有同样的影响, 定义n个不同的箱子位置,为每个箱子生成不同的模型矩阵,每个模型矩阵包含相应的本地到世界变换:

    glBindVertexArray(vao);
    for (int i=0; i<4; i++) {
        Mat4* model = new Mat4();
        model->rotate(Vec3(1, 1, 0), CC_DEGREES_TO_RADIANS(60));
        model->translate(cubpos[i]);
        GLuint modeloc = glGetUniformLocation(program->getProgram(), "model");
        glUniformMatrix4fv(modeloc, 1, GL_FALSE, model->m);
        
        //把model 矩阵 求逆 、转置后传给vertex shader
        Mat4* traninversemodel = new Mat4(*model);
        traninversemodel->inverse();
        traninversemodel->transpose();
        glUniformMatrix4fv(glGetUniformLocation(program->getProgram(), "tranInverseModel"), 1, GL_FALSE, traninversemodel->m);
        
        glDrawArrays(GL_TRIANGLES, 0, 36);
    }
    glBindVertexArray(0);


不要忘记定义光源的方向(注意,我们把方向定义为:从光源处发出的方向;在下面,你可以快速看到光的方向的指向):
glUniform3f(glGetUniformLocation(program->getProgram(), "light.direction"), -0.2, -1.0, -0.3);

点光源(Point Light)

点光是一个在时间里有位置的光源,它向所有方向发光,光线随距离增加逐渐变暗。想象灯泡和火炬作为投光物,它们可以扮演点光的角色。


衰减(Attenuation)

随着光线穿越距离的变远使得亮度也相应地减少的现象,通常称之为衰减(Attenuation)。一种随着距离减少亮度的方式是使用线性等式。这样的一个随着距离减少亮度的线性方程,可以使远处的物体更暗。然而,这样的线性方程效果会有点假。在真实世界,通常光在近处时非常亮,但是一个光源的亮度,开始的时候减少的非常快,之后随着距离的增加,减少的速度会慢下来。我们需要一种不同的方程来减少光的亮度。

下面的方程把一个片段的光的亮度除以一个已经计算出来的衰减值,这个值根据光源的远近得到:


在这里 d 代表片段到光源的距离。为了计算衰减值,我们定义3个(可配置)项:常数项 Kc ,一次项 Kl 和二次项 Kq 。

  • 常数项通常是1.0,它的作用是保证分母永远不会比1小,因为它可以利用一定的距离增加亮度,这个结果不会影响到我们所寻找的。
  • 一次项用于与距离值相乘,这会以线性的方式减少亮度。
  • 二次项用于与距离的平方相乘,为光源设置一个亮度的二次递减。二次项在距离比较近的时候相比一次项会比一次项更小,但是当距离更远的时候比一次项更大。

由于二次项的光会以线性方式减少,指导距离足够大的时候,就会超过一次项,之后,光的亮度会减少的更快。最后的效果就是光在近距离时亮度很高,但是距离变远亮度迅速降低,最后亮度降低速度再次变慢。下面的图展示了在100以内的范围,这样的衰减效果。

你可以看到当距离很近的时候光有最强的亮度,但是随着距离增大,亮度明显减弱,大约接近100的时候,就会慢下来。这就是我们想要的。

但是,我们把这三个项设置为什么值呢?正确的值的设置由很多因素决定:环境、你希望光所覆盖的距离范围、光的类型等。大多数场合,这是经验的问题,也要适度调整。下面的表格展示一些各项的值,它们模拟现实(某种类型的)光源,覆盖特定的半径(距离)。第一栏定义一个光的距离,它覆盖所给定的项。这些值是大多数光的良好开始,它是来自Ogre3D的维基的礼物:

距离 常数项 一次项 二次项
7 1.0 0.7 1.8
13 1.0 0.35 0.44
20 1.0 0.22 0.20
32 1.0 0.14 0.07
50 1.0 0.09 0.032
65 1.0 0.07 0.017
100 1.0 0.045 0.0075
160 1.0 0.027 0.0028
200 1.0 0.022 0.0019
325 1.0 0.014 0.0007
600 1.0 0.007 0.0002
3250 1.0 0.0014 0.000007

就像你所看到的,常数项 Kc 一直都是1.0。一次项 Kl 为了覆盖更远的距离通常很小,二次项 Kq 就更小了。尝试用这些值进行实验,看看它们在你的实现中各自的效果。我们的环境中,32到100的距离对大多数光通常就足够了。

为了实现衰减,在着色器中我们会需要三个额外数值:也就是公式的常量、一次项和二次项。最好把它们储存在之前定义的Light结构体中。要注意的是我们计算 lightDir ,就是在前面的教程中我们所做的,不是像之前的定向光的那部分。

//lightmap.fsh, 物体的fragment shader, 实现光照贴图

varying vec3 v_normal; //需要把法向量由顶点着色器传递到片段着色器
varying vec3 v_fragpos; //片段的位置,用以计算每个片段的法向量
varying vec2 v_texCoord; //纹理坐标

//uniform vec3 objectColor;
//uniform vec3 lightColor;
//uniform vec3 lightpos; //传入灯光的位置
uniform vec3 viewPos; //摄像机位置, 观察者的世界空间坐标,我们简单地使用摄像机对象的位置坐标代替(它就是观察者)

struct Material
{
    sampler2D diffuse; //移除amibient材质颜色向量,因为ambient颜色绝大多数情况等于diffuse颜色,所以不需要分别去储存它
    sampler2D specular;
    float shininess;
}; //材质结构体
uniform Material mater;

//点光源
struct PointLight
{
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float constant; //衰减公式的常熟
    float linear; //一次常数
    float quadratic; //2次常数
};
uniform PointLight light;

void main()
{
    vec3 texdiffuse = vec3(texture2D(mater.diffuse, v_texCoord));

    //环境光
    vec3 ambient = light.ambient * texdiffuse;
    
    //漫反射
    vec3 norm = normalize(v_normal);
    vec3 lightdir = normalize(light.position - v_fragpos);//normalize(lightpos - v_fragpos);
    float diff = max(dot(norm, lightdir), 0.0);
    vec3 diffuse = light.diffuse * diff * texdiffuse;
    
    //镜面反射
    float specularStrenth = 0.5; //镜面强度
    vec3 viewDir = normalize(viewPos - v_fragpos);
    vec3 reflectDir = reflect(-lightdir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), mater.shininess); //这个32是高光的发光值(Shininess)。一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。
    vec3 specular = light.specular * spec * vec3(texture2D(mater.specular, v_texCoord));
    
    //点光源的衰减
    float dist = length(light.position - v_fragpos);
    float attenuation = 1.0 / (light.constant + light.linear * dist + light.quadratic * dist * dist);
    ambient *= attenuation;
    diffuse *= attenuation;
    specular *= attenuation;
    
    vec3 result = ambient + diffuse + specular;
    gl_FragColor = vec4(result, 1.0);
}

在片段着色器中实现衰减很直接:我们根据公式简单的计算衰减值,在乘以ambient、diffuse和specular元素。

我们需要将光源的距离提供给公式;还记得我们是怎样计算向量的长度吗?我们可以通过获取片段和光源之间的不同向量把向量的长度结果作为距离项。我们可以使用GLSL的内建length函数做这件事.

然后,我们在OpenGL中设置这些项:我们希望光覆盖50的距离,所以我们会使用上面的表格中合适的常数项、一次项和二次项:

  glUniform3f(glGetUniformLocation(program->getProgram(), "light.position"), lightpos.x, lightpos.y, lightpos.z); //点光源的位置
    glUniform1f(glGetUniformLocation(program->getProgram(), "light.constant"), 1.0); //点光源的衰减系数
    glUniform1f(glGetUniformLocation(program->getProgram(), "light.linear"), 0.09);
    glUniform1f(glGetUniformLocation(program->getProgram(), "light.quadratic"), 0.032);


聚光(Spotlight)

聚光是一种位于环境中某处的光源,它不是向所有方向照射,而是只朝某个方向照射。结果是只有一个聚光照射方向的确定半径内的物体才会被照亮,其他的都保持黑暗。聚光的好例子是路灯或手电筒。

OpenGL中的聚光用世界空间位置,一个方向和一个指定了聚光半径的切光角来表示。我们计算的每个片段,如果片段在聚光的切光方向之间(就是在圆锥体内),我们就会把片段照亮。下面的图可以让你明白聚光是如何工作的:

  • LightDir:从片段指向光源的向量。
  • SpotDir:聚光所指向的方向。
  • Phi(Φ):定义聚光半径的切光角。每个落在这个角度之外的,聚光都不会照亮。
  • Theta(Θ):LightDir向量和SpotDir向量之间的角度。值应该比值小,这样才会在聚光内。

所以我们大致要做的是,计算LightDir向量和SpotDir向量的点乘(返回两个单位向量的点乘,还记得吗?),然后在和切光角对比。现在你应该明白聚光是我们下面将创建的手电筒的范例。

手电筒(Flashlight)是一个坐落在观察者位置的聚光,通常瞄准玩家透视图的前面。基本上说,一个手电筒是一个普通的聚光,但是根据玩家的位置和方向持续的更新它的位置和方向。

所以我们需要为片段着色器提供的值,是聚光的位置向量(来计算光的方向坐标),聚光的方向向量和切光角。我们可以把这些值储存在SpotLight结构体中:

//聚光、手电筒
struct SpotLight
{
    vec3 position; //聚光的位置向量 , 手电筒的位置
    vec3 direction; //聚光的方向向量, 手电筒的光照中心线方向
    float cutOff; //切光角 的余弦值, 聚光辐射范围角度的一半,
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float constant; //衰减公式的常熟
    float linear; //一次常数
    float quadratic; //2次常数
};
uniform SpotLight light;

我们把这些适当的值传给 片段着色器:

//聚光的参数
    glUniform3f(glGetUniformLocation(program->getProgram(), "light.position"), cam->Position.x, cam->Position.y, cam->Position.z);
    glUniform3f(glGetUniformLocation(program->getProgram(), "light.direction"), cam->Front.x, cam->Front.y, cam->Front.z);
    glUniform1f(glGetUniformLocation(program->getProgram(), "light.cutOff"), cosf(CC_DEGREES_TO_RADIANS(12.5)));
    glUniform1f(glGetUniformLocation(program->getProgram(), "light.constant"), 1.0); //聚光灯的衰减系数
    glUniform1f(glGetUniformLocation(program->getProgram(), "light.linear"), 0.09);
    glUniform1f(glGetUniformLocation(program->getProgram(), "light.quadratic"), 0.032);

我们为切光角设置一个角度,但是我们根据一个角度计算了余弦值,把这个余弦结果传给了片段着色器。这么做的原因是在片段着色器中,我们计算 LightDir 和 SpotDir 向量的点乘,而点乘返回一个余弦值,不是一个角度,所以我们不能直接把一个角度和余弦值对比。为了获得这个角度,我们必须计算点乘结果的反余弦,这个操作开销是很大的。所以为了节约一些性能,我们先计算给定切光角的余弦值,然后把结果传递给片段着色器。由于每个角度都被表示为余弦了,我们可以直接对比它们,而不用进行任何开销高昂的操作。

现在剩下要做的是计算 θ 值,用它和 ϕ 值对比,以决定我们是否在或不在聚光的内部:

vec3 lightdir = normalize(light.position - v_fragpos); //从物体到光源的方向向量
    //判断光照是否在聚光灯的照射范围内
    float theta = dot(lightdir, normalize(-light.direction));
    
    if (theta < light.cutOff) {
        vec3 texdiffuse = vec3(texture2D(mater.diffuse, v_texCoord));
        
        //环境光
        vec3 ambient = light.ambient * texdiffuse;
        
        //漫反射
        vec3 norm = normalize(v_normal);
        float diff = max(dot(norm, lightdir), 0.0);
        vec3 diffuse = light.diffuse * diff * texdiffuse;
        
        //镜面反射
        float specularStrenth = 0.5; //镜面强度
        vec3 viewDir = normalize(viewPos - v_fragpos);
        vec3 reflectDir = reflect(-lightdir, norm);
        float spec = pow(max(dot(viewDir, reflectDir), 0.0), mater.shininess); //这个32是高光的发光值(Shininess)。一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。
        vec3 specular = light.specular * spec * vec3(texture2D(mater.specular, v_texCoord));
        
        //点光源的衰减
        float dist = length(light.position - v_fragpos);
        float attenuation = 1.0 / (light.constant + light.linear * dist + light.quadratic * dist * dist);
        ambient *= attenuation;
        diffuse *= attenuation;
        specular *= attenuation;
        
        vec3 result = ambient + diffuse + specular;
        gl_FragColor = vec4(result, 1.0);
    }
    else{
        gl_FragColor = vec4(light.ambient + vec3(texture2D(mater.diffuse, v_texCoord)), 1.0);
    }

我们首先计算 lightDir 和取反的 direction 向量的点乘(它是取反过的因为我们想要向量指向光源,而不是从光源作为指向出发点。 确保对所有相关向量进行了标准化处理。

if条件中使用>符号还是<符号?为了在聚光以内,theta不是应该比光的切光值更小吗?这没错,但是不要忘了,角度值是以余弦值来表示的,一个0度的角表示为1.0的余弦值,当一个角是90度的时候被表示为0.0的余弦值,你可以在这里看到:

现在你可以看到,余弦越是接近1.0,角度就越小。这就解释了为什么θ需要比切光值更大了。切光值当前被设置为12.5的余弦,它等于0.9978,所以θ的余弦值在0.9979和1.0之间,片段会在聚光内,被照亮。


平滑边缘

片段着色器一旦到达了聚光的圆锥边缘,它就立刻黑了下来,却没有任何平滑减弱的过度。一个真实的聚光的光会在它的边界处平滑减弱的。为创建聚光的平滑边,我们希望去模拟的聚光有一个内圆锥和外圆锥。我们可以把内圆锥设置为前面部分定义的圆锥,我们希望外圆锥从内边到外边逐步的变暗。

为创建外圆锥,我们简单定义另一个余弦值,它代表聚光的方向向量和外圆锥的向量(等于它的半径)的角度。然后,如果片段在内圆锥和外圆锥之间,就会给它计算出一个0.0到1.0之间的亮度。如果片段在内圆锥以内这个亮度就等于1.0,如果在外面就是0.0。

我们可以使用下面的公式计算这样的值:


   float epsilon = light.cutOff - light.outerCutOff;
    float intensity = 0.0;
    if (theta > light.cutOff) {
        intensity = 1.0;
    }else if(theta > light.outerCutOff){
        intensity = (theta - light.outerCutOff) / epsilon;
    }else{
        intensity = 0.0;
    }

片段着色器代码:

//spotlight.fsh, 实现聚光, 手电筒效果,加边缘光滑效果

varying vec3 v_normal; //需要把法向量由顶点着色器传递到片段着色器
varying vec3 v_fragpos; //片段的位置,用以计算每个片段的法向量
varying vec2 v_texCoord; //纹理坐标

//uniform vec3 objectColor;
//uniform vec3 lightColor;
//uniform vec3 lightpos; //传入灯光的位置
uniform vec3 viewPos; //摄像机位置, 观察者的世界空间坐标,我们简单地使用摄像机对象的位置坐标代替(它就是观察者)

struct Material
{
    sampler2D diffuse; //移除amibient材质颜色向量,因为ambient颜色绝大多数情况等于diffuse颜色,所以不需要分别去储存它
    sampler2D specular;
    float shininess;
}; //材质结构体
uniform Material mater;

//聚光、手电筒
struct SpotLight
{
    vec3 position; //聚光的位置向量 , 手电筒的位置
    vec3 direction; //聚光的方向向量, 手电筒的光照中心线方向
    float cutOff; //切光角 的余弦值, 聚光辐射范围角度的一半,
    float outerCutOff; //外圆锥的切广角的余弦值
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float constant; //衰减公式的常熟
    float linear; //一次常数
    float quadratic; //2次常数
};
uniform SpotLight light;

void main()
{
    vec3 lightdir = normalize(light.position - v_fragpos); //从物体到光源的方向向量
    //判断光照是否在聚光灯的照射范围内
    float theta = dot(lightdir, normalize(-light.direction));
    float epsilon = light.cutOff - light.outerCutOff;
//    float intensity = clamp((theta - light.outerCutOff) / epsilon,0.0, 1.0);
    float intensity = 0.0;
    if (theta > light.cutOff) { //在内圆锥以内这个亮度就等于1.0
        intensity = 1.0;
    }else if(theta > light.outerCutOff){ //在内圆锥和外圆锥之间,就会给它计算出一个0.0到1.0之间的亮度
        intensity = (theta - light.outerCutOff) / epsilon;
    }else{ //在外圆锥之外就是0.0
        intensity = 0.0;
    }
    
    
    vec3 texdiffuse = vec3(texture2D(mater.diffuse, v_texCoord));
    
    //环境光
    vec3 ambient = light.ambient * texdiffuse;
    
    //漫反射
    vec3 norm = normalize(v_normal);
    float diff = max(dot(norm, lightdir), 0.0);
    vec3 diffuse = light.diffuse * diff * texdiffuse;
    diffuse *= intensity;
    
    //镜面反射
    float specularStrenth = 0.5; //镜面强度
    vec3 viewDir = normalize(viewPos - v_fragpos);
    vec3 reflectDir = reflect(-lightdir, norm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), mater.shininess); //这个32是高光的发光值(Shininess)。一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。
    vec3 specular = light.specular * spec * vec3(texture2D(mater.specular, v_texCoord));
    specular *= intensity;
    
    //点光源的衰减
    float dist = length(light.position - v_fragpos);
    float attenuation = 1.0 / (light.constant + light.linear * dist + light.quadratic * dist * dist);
    ambient *= attenuation;
    diffuse *= attenuation;
    specular *= attenuation;
    
    vec3 result = ambient + diffuse + specular;
    gl_FragColor = vec4(result, 1.0);
 
}


多光源

当我们在场景中使用多个光源时一般使用以下途径:创建一个代表输出颜色的向量。每一个光源都对输出颜色贡献一些颜色。因此,场景中的每个光源将进行独立运算,并且运算结果都对最终的输出颜色有一定影响。下面是使用这种方式进行多光源运算的一般结构:

void main()
{
    // 一些属性
    vec3 norm = normalize(v_normal);
    vec3 viewDir = normalize(viewPos - v_fragpos);
    
    // 第一步,计算平行光照
    vec3 result = makeDirLight(dirLight, norm, viewDir);
    // 第二步,计算顶点光照
    for(int i = 0; i < ptlightsNum; i++){
        result += makePointLight(pointLights[i], norm, v_fragpos, viewDir);
    }
    // 第三步,计算 Spot light
    result += makeSpotLight(spotLight, norm, v_fragpos, viewDir);
    
    gl_FragColor = vec4(result, 1.0);
}

然后为每种光定义一种结构体,再定义一种 函数 来计算光照。片段着色器:

//multilight.fsh, 多种光源综合使用,平行光(阳光)、点光源(蜡烛光)、聚光灯(手电筒)。

varying vec3 v_normal; //需要把法向量由顶点着色器传递到片段着色器
varying vec3 v_fragpos; //片段的位置,用以计算每个片段的法向量
varying vec2 v_texCoord; //纹理坐标

uniform vec3 viewPos; //摄像机位置, 观察者的世界空间坐标,我们简单地使用摄像机对象的位置坐标代替(它就是观察者)

struct Material
{
    sampler2D diffuse; //移除amibient材质颜色向量,因为ambient颜色绝大多数情况等于diffuse颜色,所以不需要分别去储存它
    sampler2D specular;
    float shininess;
}; //材质结构体
uniform Material mater;
//定向光
struct DirectionLight
{
    vec3 direction;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform DirectionLight dirLight;
//点光源
struct PointLight
{
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float constant; //衰减公式的常熟
    float linear; //一次常数
    float quadratic; //2次常数
};
#define ptlightsNum 4
uniform PointLight pointLights[ptlightsNum]; //定义4组点光源
//聚光、手电筒
struct SpotLight
{
    vec3 position; //聚光的位置向量 , 手电筒的位置
    vec3 direction; //聚光的方向向量, 手电筒的光照中心线方向
    float cutOff; //切光角 的余弦值, 聚光辐射范围角度的一半,
    float outerCutOff; //外圆锥的切广角的余弦值
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
    float constant; //衰减公式的常熟
    float linear; //一次常数
    float quadratic; //2次常数
};
uniform SpotLight spotLight;

//平行光计算
vec3 makeDirLight(DirectionLight light, vec3 normal, vec3 viewDir)
{
    vec3 texdiffuse = vec3(texture2D(mater.diffuse, v_texCoord));
    
    //环境光
    vec3 ambient = light.ambient * texdiffuse;
    
    //漫反射
    vec3 lightdir = normalize(-light.direction);//normalize(lightpos - v_fragpos);
    float diff = max(dot(normal, lightdir), 0.0);
    vec3 diffuse = light.diffuse * diff * texdiffuse;
    
    //镜面反射
    vec3 reflectDir = reflect(-lightdir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), mater.shininess); //这个32是高光的发光值(Shininess)。一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。
    vec3 specular = light.specular * spec * vec3(texture2D(mater.specular, v_texCoord));
    
    vec3 result = ambient + diffuse + specular;
    return result;
}

//计算点光源(Point Light)
vec3 makePointLight(PointLight light, vec3 normal, vec3 fragpos, vec3 viewDir)
{
    vec3 texdiffuse = vec3(texture2D(mater.diffuse, v_texCoord));
    
    //环境光
    vec3 ambient = light.ambient * texdiffuse;
    
    //漫反射
    vec3 lightdir = normalize(light.position - fragpos);
    float diff = max(dot(normal, lightdir), 0.0);
    vec3 diffuse = light.diffuse * diff * texdiffuse;
    
    //镜面反射
    vec3 reflectDir = reflect(-lightdir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), mater.shininess); //这个32是高光的发光值(Shininess)。一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。
    vec3 specular = light.specular * spec * vec3(texture2D(mater.specular, v_texCoord));
    
    //点光源的衰减
    float dist = length(light.position - fragpos);
    float attenuation = 1.0 / (light.constant + light.linear * dist + light.quadratic * dist * dist);
    ambient *= attenuation;
    diffuse *= attenuation;
    specular *= attenuation;
    
    vec3 result = ambient + diffuse + specular;
    return result;
}

vec3 makeSpotLight(SpotLight light, vec3 normal, vec3 fragpos, vec3 viewDir)
{
    vec3 lightdir = normalize(light.position - fragpos); //从物体到光源的方向向量
    //判断光照是否在聚光灯的照射范围内
    float theta = dot(lightdir, normalize(-light.direction));
    float epsilon = light.cutOff - light.outerCutOff;
    float intensity = 0.0;
    if (theta > light.cutOff) { //在内圆锥以内这个亮度就等于1.0
        intensity = 1.0;
    }
    else if(theta > light.outerCutOff){ //在内圆锥和外圆锥之间,就会给它计算出一个0.0到1.0之间的亮度
        intensity = (theta - light.outerCutOff) / epsilon;
    }else{ //在外圆锥之外就是0.0
        intensity = 0.0;
    }
    
    
    vec3 texdiffuse = vec3(texture2D(mater.diffuse, v_texCoord));
    
    //环境光
    vec3 ambient = light.ambient * texdiffuse;
    
    //漫反射
    float diff = max(dot(normal, lightdir), 0.0);
    vec3 diffuse = light.diffuse * diff * texdiffuse;
    diffuse *= intensity;
    
    //镜面反射
    vec3 reflectDir = reflect(-lightdir, normal);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), mater.shininess); //这个32是高光的发光值(Shininess)。一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。
    vec3 specular = light.specular * spec * vec3(texture2D(mater.specular, v_texCoord));
    specular *= intensity;
    
    //点光源的衰减
    float dist = length(light.position - fragpos);
    float attenuation = 1.0 / (light.constant + light.linear * dist + light.quadratic * dist * dist);
    ambient *= attenuation;
    diffuse *= attenuation;
    specular *= attenuation;
    
    vec3 result = ambient + diffuse + specular;
    return result;
}

void main()
{
    // 一些属性
    vec3 norm = normalize(v_normal);
    vec3 viewDir = normalize(viewPos - v_fragpos);
    
    // 第一步,计算平行光照
    vec3 result = makeDirLight(dirLight, norm, viewDir);
    // 第二步,计算顶点光照
    for(int i = 0; i < ptlightsNum; i++){
        result += makePointLight(pointLights[i], norm, v_fragpos, viewDir);
    }
    // 第三步,计算 Spot light
    result += makeSpotLight(spotLight, norm, v_fragpos, viewDir);
    
    gl_FragColor = vec4(result, 1.0);
}

全部代码:

//
//  OpenGLMultiLight.cpp
//  shaderTest
//  
//  Created by MacSBL on 2017/1/10.
//
//

#include "OpenGLMultiLight.h"

bool OpenGLMultiLight::init()
{
    if (!Layer::init()) {
        return false;
    }
    origin = Director::getInstance()->getVisibleOrigin();
    vsize = Director::getInstance()->getVisibleSize();
    
    GLfloat vertices[] = {
        // Positions           // Normals           // Texture Coords
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,
        0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 0.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f,  0.0f, -1.0f,  0.0f, 0.0f,
        
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,
        0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f,  0.0f, 1.0f,   0.0f, 0.0f,
        
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f, -1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
        
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
        0.5f,  0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  0.0f, 0.0f,
        0.5f,  0.5f,  0.5f,  1.0f,  0.0f,  0.0f,  1.0f, 0.0f,
        
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,
        0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 1.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
        0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, -1.0f,  0.0f,  0.0f, 1.0f,
        
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f,
        0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 1.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
        0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f,  1.0f,  0.0f,  0.0f, 1.0f
    };
//    auto difsprite = Sprite::create("container2.png");
//    diffuseTexture = difsprite->getTexture()->getName();
    
    auto spcsprite = Sprite::create("container2_specular.png");
    specularTexture = spcsprite->getTexture()->getName();
    
    //以下方法绑定纹理失败,原因: glTexImage2D 中的2个 GL_RGB 都改为 GL_RGBA即可。 跟图片是否有alpha通道有关??
    //绑定纹理
    glGenTextures(1, &diffuseTexture);
    glBindTexture(GL_TEXTURE_2D, diffuseTexture);
    
   
    //纹理环绕方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    //纹理过滤
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

     //加载纹理
    Image* img = new Image();
    img->initWithImageFile("container2.png");
    GLsizei width = img->getWidth();
    GLsizei height = img->getHeight();
    unsigned char* imgdata = img->getData();
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imgdata);
    glGenerateMipmap(GL_TEXTURE_2D);
    //释放内存,解绑texture
    CC_SAFE_DELETE(img);
    glBindTexture(GL_TEXTURE_2D, 0);
 
    //——————————————————————————————————————————————————————————————————————————————————————————————
    //设置 glprogram
    auto program = new GLProgram();
    program->initWithFilenames("directionlight.vsh", "multilight.fsh");
    program->link();
    this->setGLProgram(program);
    //——————————————————————————————————————————————————————————————————————————————————————————————
    
    //物体的vao
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    
    GLuint vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
    
    //把顶点数据传给 object.vsh vertex shader 的第一个属性(索引为0)
    GLuint posloc = glGetAttribLocation(program->getProgram(), "a_position");
    glVertexAttribPointer(posloc, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(posloc);
    
    //把法向量传给 object.vsh 的a_normal属性,注意:这里不能想当然的认为vertex shader中第2个attribute就是索引为1,必须通过program实事求是的取出它的location
    GLuint normloc = glGetAttribLocation(program->getProgram(), "a_normal");
    glEnableVertexAttribArray(normloc);
    glVertexAttribPointer(normloc, 3, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (GLvoid*)(3*sizeof(GLfloat)));
    
    //纹理坐标传给 vertex shader
    GLuint texloc = glGetAttribLocation(program->getProgram(), "a_texCoord");
    glEnableVertexAttribArray(texloc);
    glVertexAttribPointer(texloc, 2, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (GLvoid*)(6*sizeof(GLfloat)));
    
    glBindVertexArray(0);
    
    
    //灯光的vao, vbo 跟上边一样,灯光和物体使用同一个顶点数组
    glGenVertexArrays(1, &lightvao);
    glBindVertexArray(lightvao);
    
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    
    //把顶点数据传给 light.vsh vertex shader 的第一个属性(索引为0)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    
    glBindVertexArray(0);
    //——————————————————————————————————————————————————————————————————————————————————————————————
    
    lamProgram = new GLProgram();
    lamProgram->initWithFilenames("lamp.vsh", "lamp.fsh");
    lamProgram->link();
    
    //——————————————————————————————————————————————————————————————————————————————————————————————
    //设置转换矩阵
//    model = new Mat4();
//    model->rotate(Vec3(1, 1, 0), CC_DEGREES_TO_RADIANS(60));
    
    //camera
    cam = new MyCamera(Vec3(0, 0, 3));
    view = cam->GetViewMatrix();
    
    projection = new Mat4();
    Mat4::createPerspective(cam->Zoom, vsize.width / vsize.height, 0.1, 1000, projection);
    
//    lightpos = Vec3(0.6, 0.5, 0.7);
    
    Director::getInstance()->setDepthTest(true);
    
    cubpos[0] = {0, 0, 0};
    cubpos[1] = {2, -0.5, -3};
    cubpos[2] = {-1, -2, -2};
    cubpos[3] = {1, 0.5, -1.6};
    
    
    pointLightPos[0] = Vec3( 0.7f,  0.2f,  2.0f);
    pointLightPos[1] = Vec3( 2.3f, -3.3f, -4.0f);
    pointLightPos[2] = Vec3(-4.0f,  2.0f, -12.0f);
    pointLightPos[3] = Vec3( 0.0f,  0.0f, -3.0f);
    
    
    //touch事件
    auto elistener = EventListenerTouchOneByOne::create();
    elistener->onTouchBegan = CC_CALLBACK_2(OpenGLMultiLight::onTouchBegan, this);
    elistener->onTouchMoved = CC_CALLBACK_2(OpenGLMultiLight::onTouchMoved, this);
    elistener->onTouchEnded = CC_CALLBACK_2(OpenGLMultiLight::onTouchEnded, this);
    _eventDispatcher->addEventListenerWithSceneGraphPriority(elistener, this);
    
    return true;
}

void OpenGLMultiLight::visit(cocos2d::Renderer *render, const cocos2d::Mat4 &parentTransform, uint32_t parentflag)
{
    Layer::visit(render, parentTransform, parentflag);
    _command.init(_globalZOrder);
    _command.func = CC_CALLBACK_0(OpenGLMultiLight::onDraw, this);
    Director::getInstance()->getRenderer()->addCommand(&_command);
}

void OpenGLMultiLight::onDraw()
{
    auto program = this->getGLProgram();
    program->use();
    
    
    //物体的 model、view、project 矩阵
    view = cam->GetViewMatrix();
    Mat4::createPerspective(cam->Zoom, vsize.width / vsize.height, 0.1, 1000, projection);
    
    
    
    GLuint viewloc = glGetUniformLocation(program->getProgram(), "view");
    glUniformMatrix4fv(viewloc, 1, GL_FALSE, view->m);
    
    GLuint proloc = glGetUniformLocation(program->getProgram(), "projection");
    glUniformMatrix4fv(proloc, 1, GL_FALSE, projection->m);
    
    
    
    //direction light
    glUniform3f(glGetUniformLocation(program->getProgram(), "dirLight.direction"), -0.2, -1.0, -0.3);
    glUniform3f(glGetUniformLocation(program->getProgram(), "dirLight.ambient"), 0.05, 0.05, 0.05);
    glUniform3f(glGetUniformLocation(program->getProgram(), "dirLight.diffuse"), 0.4, 0.4, 0.4);
    glUniform3f(glGetUniformLocation(program->getProgram(), "dirLight.specular"), 0.5, 0.5, 0.5);
    
    //4个point light
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[0].position"), pointLightPos[0].x, pointLightPos[0].y, pointLightPos[0].z); //点光源的位置
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[0].ambient"), 0.05, 0.05, 0.05);
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[0].diffuse"), 0.8, 0.8, 0.8);
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[0].specular"), 1, 1, 1);
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[0].constant"), 1.0); //点光源的衰减系数
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[0].linear"), 0.09);
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[0].quadratic"), 0.032);
    
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[1].position"), pointLightPos[1].x, pointLightPos[1].y, pointLightPos[1].z); //点光源的位置
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[1].ambient"), 0.05, 0.05, 0.05);
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[1].diffuse"), 0.8, 0.8, 0.8);
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[1].specular"), 1, 1, 1);
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[1].constant"), 1.0); //点光源的衰减系数
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[1].linear"), 0.09);
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[1].quadratic"), 0.032);
    
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[2].position"), pointLightPos[2].x, pointLightPos[2].y, pointLightPos[2].z); //点光源的位置
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[2].ambient"), 0.05, 0.05, 0.05);
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[2].diffuse"), 0.8, 0.8, 0.8);
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[2].specular"), 1, 1, 1);
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[2].constant"), 1.0); //点光源的衰减系数
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[2].linear"), 0.09);
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[2].quadratic"), 0.032);
    
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[3].position"), pointLightPos[3].x, pointLightPos[3].y, pointLightPos[3].z); //点光源的位置
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[3].ambient"), 0.05, 0.05, 0.05);
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[3].diffuse"), 0.8, 0.8, 0.8);
    glUniform3f(glGetUniformLocation(program->getProgram(), "pointLights[3].specular"), 1, 1, 1);
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[3].constant"), 1.0); //点光源的衰减系数
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[3].linear"), 0.09);
    glUniform1f(glGetUniformLocation(program->getProgram(), "pointLights[3].quadratic"), 0.032);


    //聚光spot light 的参数
    glUniform3f(glGetUniformLocation(program->getProgram(), "spotLight.position"), cam->Position.x, cam->Position.y, cam->Position.z);
    glUniform3f(glGetUniformLocation(program->getProgram(), "spotLight.direction"), cam->Front.x, cam->Front.y, cam->Front.z);
    glUniform1f(glGetUniformLocation(program->getProgram(), "spotLight.cutOff"), cosf(CC_DEGREES_TO_RADIANS(12.5)));
    glUniform1f(glGetUniformLocation(program->getProgram(), "spotLight.outerCutOff"), cosf(CC_DEGREES_TO_RADIANS(17.5)));
    glUniform3f(glGetUniformLocation(program->getProgram(), "spotLight.ambient"), 0.05, 0.05, 0.05);
    glUniform3f(glGetUniformLocation(program->getProgram(), "spotLight.diffuse"), 1, 1, 1);
    glUniform3f(glGetUniformLocation(program->getProgram(), "spotLight.specular"), 1, 1, 1);
    glUniform1f(glGetUniformLocation(program->getProgram(), "spotLight.constant"), 1.0); //聚光灯的衰减系数
    glUniform1f(glGetUniformLocation(program->getProgram(), "spotLight.linear"), 0.09);
    glUniform1f(glGetUniformLocation(program->getProgram(), "spotLight.quadratic"), 0.032);

    
    GL::bindTexture2DN(0, diffuseTexture);
    glUniform1i(glGetUniformLocation(program->getProgram(), "mater.diffuse"), 0);
    GL::bindTexture2DN(1, specularTexture);
    glUniform1i(glGetUniformLocation(program->getProgram(), "mater.specular"), 1);
    glUniform1f(glGetUniformLocation(program->getProgram(), "mater.shininess"), 64.0);
    
    GLuint viewposloc = glGetUniformLocation(program->getProgram(), "viewPos");
    glUniform3f(viewposloc, cam->Position.x, cam->Position.y, cam->Position.z);
    
    
    glBindVertexArray(vao);
    for (int i=0; i<4; i++) {
        Mat4* model = new Mat4();
        model->rotate(Vec3(1, 1, 0), CC_DEGREES_TO_RADIANS(60));
        model->translate(cubpos[i]);
        GLuint modeloc = glGetUniformLocation(program->getProgram(), "model");
        glUniformMatrix4fv(modeloc, 1, GL_FALSE, model->m);
        
        //把model 矩阵 求逆 、转置后传给vertex shader
        Mat4* traninversemodel = new Mat4(*model);
        traninversemodel->inverse();
        traninversemodel->transpose();
        glUniformMatrix4fv(glGetUniformLocation(program->getProgram(), "tranInverseModel"), 1, GL_FALSE, traninversemodel->m);
        
        glDrawArrays(GL_TRIANGLES, 0, 36);
    }
    glBindVertexArray(0);
    
    //灯光的model、view、project 矩阵
    lamProgram->use();
    
    
    viewloc = glGetUniformLocation(lamProgram->getProgram(), "view");
    glUniformMatrix4fv(viewloc, 1, GL_FALSE, view->m);
    
    proloc = glGetUniformLocation(lamProgram->getProgram(), "projection");
    glUniformMatrix4fv(proloc, 1, GL_FALSE, projection->m);
    
    glBindVertexArray(lightvao);
    for (int i=0; i<4; i++) {
        GLuint modeloc = glGetUniformLocation(lamProgram->getProgram(), "model");
        auto lightmodel = new Mat4();
        lightmodel->translate(pointLightPos[i]);
        lightmodel->rotate(Vec3(0, 1, 1), CC_DEGREES_TO_RADIANS(30));
        lightmodel->scale(0.2);
        glUniformMatrix4fv(modeloc, 1, GL_FALSE, lightmodel->m);
        glDrawArrays(GL_TRIANGLES, 0, 36);
    }
    
    glBindVertexArray(0);
}
#pragma mark touch

bool OpenGLMultiLight::onTouchBegan(cocos2d::Touch *touch, cocos2d::Event *evt)
{
    return true;
}

void OpenGLMultiLight::onTouchMoved(cocos2d::Touch *touch, cocos2d::Event *evt)
{
    Vec2 curpos = touch->getLocationInView();
    Vec2 prepos = touch->getPreviousLocationInView();
    GLfloat dx = curpos.x - prepos.x;
    GLfloat dy = curpos.y - prepos.y;
    
    //移动摄像机
    GLfloat camspeed = 0.05f;
        if (curpos.y - prepos.y > 0) { //w
            cam->ProcessKeyboard(Cam_Move::FORWARD, camspeed);
        }else if (curpos.y - prepos.y < 0){ //s
            cam->ProcessKeyboard(Cam_Move::BACKWARD, camspeed);
        }
        else if (curpos.x - prepos.x < 0){ //a
             cam->ProcessKeyboard(Cam_Move::LEFT, camspeed);
        }else if (curpos.x - prepos.x > 0){ //d
            cam->ProcessKeyboard(Cam_Move::RIGHT, camspeed);
        }
    //(3)旋转摄像机
    
//    cam->ProcessMouseMovement(dx, dy);
           //(4)缩放
//    if(fov >= 1 && fov <= 45){
//        fov -= dx * camspeed;
//    }
//    if(fov <= 1){
//        fov = 1;
//    }
//    if(fov >= 45){
//        fov = 45;
//    }
}

void OpenGLMultiLight::onTouchEnded(cocos2d::Touch *touch, cocos2d::Event *evt)
{
    
}





comsol学习PPT COMSOL_MULTIPHYSICS_TJU_来学校——西安交通大学讲座的PPY 立即下载

相关推荐

aa.rar_comsol_comsol光源_comsol点光源

2点光源相干的comsol模型,呵呵呵,挺有帮助的

如何可以算作入门了COMSOL?

COMSOL Multiphysics具有高效的计算性能和独特的多物理场全耦合分析能力,可以保证数值仿真的高度精确,因此被应用于各个学科领域。 那么怎么样才算是入门了COMSOL 呢? 首先要了解仿真框架建立及软件基本操作 包括COMSOL软件基本操作 参数及变量设置方法 几何建模 基本函数设置方法,如插值函数、解析函数、分段函数等 特殊函数的设置方法,如积分、求极值、求平均值等 网格划分 以及前处理和后处理的技巧 特殊变量的定义,如散射截面等 如何利用软件的绘图功能绘制不同类型的数据图 数据导出 不同类型

weixin_56489496的博客 800

SERS_ping_hexagonal_1.rar_comsol SERS_comsol 三维结构_comsol 周期_co

COMSOL计算三维周期圆柱结构,透射率和反射率模型

(133)定向光源

定向光源 模拟从一个无限远的源头处发出的光照。这意味着这个光源照射出阴影的效果都是平行的,从而使得它成了模拟太阳光的理想选择。定向光源在设置的时候可以选择三种移动设置的一种。 静态 - (左侧的图片)意味着游戏时光照无法被修改。这是渲染效率最快的一种形式,并能采用烘培光照。 固定 - (同样是左侧图片)意味着光照产生的阴影以及由 Lightmass 计算的静态物

革命队伍的螺丝钉 6663

CATIA CAA开发环境搭建避坑指南:从授权服务到VS配置全流程

本文提供了一份详尽的CATIA CAA开发环境搭建避坑指南,涵盖从授权服务(DSLS)安装、Visual Studio版本匹配与集成配置,到编译调试全流程。重点解析了授权服务无法连接、模块加载失败等典型问题的解决方案,强调版本严格对应与纯英文环境是成功搭建的关键。

redis7keeper的博客 573

工程师必看:如何用功率谱密度搞定随机振动测试(附Grms计算实例)

本文为工程师详解如何通过功率谱密度(PSD)曲线计算Grms(总均方根加速度),从而量化随机振动测试的强度。文章拆解了平直谱、斜谱的计算公式,提供了完整的Grms计算实例,并指出了单位换算、对数坐标等常见工程陷阱,帮助工程师精准完成测试准备与设备推力评估。

3c4x5z6v7b的博客 627

js怎么模拟键盘输入_来阿、来阿、都来看看three.js中的几种光源有何不同

在three.js中如果不设置光源,就算几何体设置了颜色,看到的物体也将会是漆黑一片。但是three中有分为好几种光源,他有什么不同呢,接下来我们一起来看一下这几种光源的不同之处。AmbintlightTHREE.AmbientLight 环境光,是一种最常见的光源。环境光会均匀照亮所有物体,它 没有投影效果 ,因为它是没有方向的。// 环境光 参数1:光线颜色 参数2:关照强...

weixin_32375895的博客 223

西红柿成熟度检测数据集总结

成熟度类别标注数量semiripe(半成熟)10275rotten(腐烂)13470fullyripe(完全成熟)35031unripe(未成熟)26212。

crz1336768325的博客 2125

Unity中的光源有哪几种?分别是什么?

Unity中一共提供了四种光源,分别是:   1.  方向光:Directional Light 用于模拟太阳,模拟自然光,方向光任何地方都能照射到,就和太阳一样,但是要注意照射方向。   2.  点光源:Point Light 用于模拟电灯泡的照射效果。   3.  聚光灯:Spot Light 用于模拟聚光灯照射效果。   4.  区域光:Area Light 区域光在...

慕峰的博客 1万+

Linux SMP系统多核启动机制详解

对称多处理(SMP)是现代操作系统支持多核并行计算的基础架构。其核心原理是通过引导处理器(BSP)初始化系统环境后,通过处理器间中断(IPI)机制唤醒应用处理器(AP)。在x86架构中,AP启动时处于实模式,需要特殊的trampoline代码完成保护模式切换和分页设置。这种设计既保证了系统初始化的正确性,又实现了多核并行处理的性能优势。理解SMP启动流程对优化系统启动速度和调试多核问题至关重要,特别是在云计算和嵌入式系统中,高效的SMP初始化能显著提升系统响应能力。本文以Linux内核为例,深入解析了从BS

weixin_35006125的博客 291

[NoC]Outstanding和Credit的概念详解

摘要:本文详细解析了计算机体系结构中outstanding和credit两个核心概念。outstanding指已发出但未完成的请求数量,通过跟踪未决事务实现并发和乱序执行,常见于AXI总线等场景;credit则是基于信用令牌的流控机制,通过控制发送速率防止接收端缓冲溢出,多用于NoC网络。二者在目的、机制上存在差异:outstanding侧重提升并发度,credit关注流控保护。文章通过AXI协议实例和SystemC代码示例展示了二者的应用与结合方式,并探讨了在SoC设计中参数优化的方法,包括性能/资源权衡

元直的博客 2284

Unity引擎中光源都有哪几种,都有什么作用

3. 聚光灯(Spotlights):聚光灯有明确的位置,同时也有明确的照亮方向,发出的光束内的物体会被照亮,发出的光束外的物体则不会被照亮。Unity引擎为了实现游戏场景的明暗和光影效果,提供了四种类型的光源,分别是方向光(Directional Lights)、点光源(Point Lights)、聚光灯(Spotlights)以及区域光(Area Light)。对于Unity中的灯光,可以设置颜色、强度、照射角度等各种参数,同时可以应用在物理材质上,更为真实地模拟出现实中的光照条件。

2401_82584055的博客 887

【电源专题】案例:对比不同电池在不同电流下的电池放电曲线

无

一点硬件 2842

Three.js中有哪的几种光源,每种光源分别代表什么意思???(讲解十分详细)

在three.js中一共有四种光源,分别是一下四种: 环境光( AmbientLight ):笼罩在整个空间无处不在的光 点光源( PointLight ):向四面八方发射的单点光源 聚光灯( SpotLight ):发射出锥形状的光, 模拟手电筒,台灯等光源 平行光( DirectinalLight ):平行的一束光,模拟从很远处照射的太阳光 环境光 境光可以说是场景的整体基调。如何创建环境光呢?代码如下。 var ambient = Three.AmbientLight(0xffffff);

qq_42005992的博客 817

YOLOv8改进系列:RepVGG重参数化结构——训练多分支,推理单路,极致提速

本文将详细介绍如何将RepVGG的重参数化思想融入到YOLOv8的骨干网络中,实现一种训练时多分支、推理时单路的改进版YOLOv8。我们将提供完整的代码实现、详细的原理讲解,并在COCO数据集和自定义数据集上进行充分的实验验证。

YOLO 200

IEC61850规约客户端软件开发实战(第二章)

本文介绍了基于IEC61850规约的客户端软件开发过程,重点阐述功能界面设计和服务端连接功能的实现。界面采用Qt框架设计,包含功能按钮区、数据模型展示区(使用QTreeWidget)和状态显示区。连接功能通过libiec61850开源库实现,详细说明了连接流程、句柄创建及状态管理的代码实现。测试结果表明系统能正确识别连接状态,并通过界面直观显示。文章为IEC61850规约软件开发提供了实用参考,下期将分享规约模型数据加载的实现过程。

半青年的博客 2859

Unity面试:Unity提供了几种光源,分别是什么?

实际工作中,开发者通常会根据场景的需求,合理结合使用这些光源,以达到最佳的光照效果和性能优化。在Unity 3D游戏开发中,光源是场景中重要的组成部分,它们可以影响对象的外观和气氛。:光照探针并不是光源,它们用于间接光照的烘焙,帮助静态对象接收场景中的全局光照。:方向光源模拟无限远的光源(如太阳),光线是平行的,照射到场景中的物体表面。:虽然严格来说发光材质不是普通的光源,但它能在场景中模拟物体自身发光的效果。不会在场景中产生具体的光源位置,光照效果取决于光源的方向。常用于户外场景,给场景提供全局的光照。

m0_67537062的博客 706

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

THREE.JS中常用的4种光源

参考https://www.2cto.com/kf/201605/507002.html 总序 如果没有光源,就不可能看到任何渲染结果。 AmbientLight: 环境光,基础光源,它的颜色会被加载到整个场景和所有对象的当前颜色上。PointLight:点光源,朝着所有方向都发射光线SpotLight:聚光灯光源:类型台灯,天花板上的吊灯,手电筒等DirectionalLight:...

aoxuestudy的专栏 789

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

各种光源类型及图片

各种光源的样式,做个笔记。类型引自https://blog.youkuaiyun.com/xuxunjie147/article/details/79109393?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_baidulandingword-1&spm=1001.2101.3001.4242,更具体说明和应用场合请移步过去看吧。 所用图片来自百度搜索。 其它类型待添加。 1、环形光源 环形光源主要应用领域:PCB基板检测,IC元件检测

sl961102的博客 2848

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

第一章节: 光源种类介绍

光源类型 本单介绍光源的几种类型 不同的光源有其自身发展的需求,目前常见到的光源有如下: IES 工业光源, LDT 光源, CG光源,以及IBL光源; 在进行光照系统的模拟计算时,光源的建模精度,直接决定了模拟光照效果的准确性,一般需要描述光源的发光强度,也就是在空间的辐射光线的分布, 还有光源的光谱特征。 先说一下与光源有关的基本术语 辐射照度(Irradiance) 在辐射接收面上一点的...

大白的博客 1815

threejs光源

没有光源,渲染场景将不可见。threejs中已经帮我们实现了大量的光源,我们可以直接使用,主要分为两类:基础光源和特殊光源,下面将依次详细介绍。

疯狂的程序员 943

光源基础(1)——常见光源性能比对和好图像评价指标

常见光源性能比对(氙灯、高频荧光灯、光纤卤素灯、发光二极管)和好图像评价指标(对比度、均匀性、一致性等)

大胡子大叔的专栏 1339

光源的闪烁效应及闪烁的测量方法

来源:http://hi.baidu.com/allendada/blog/item/21dc81384e3b2cf5b311c765.html摘 要:本文介绍了光源闪烁的基本概念,产生的原因,并探讨了光源闪烁及闪烁效应的各种危害。阐述了闪烁的评价    方法和测量手段,最后测量了几种典型光源的闪烁强度,其数据及分析结果可以作为健康照明中的参考。关键词:闪烁 频闪效应 波动

smallmuou的专栏 4241

[激光原理与应用-35]:《光电检测技术-2》- 光学测量基础 - 认识光源

目录一、对光源的基本要求二、光源的定义三、光源的分类四、产生途径五、电光效应发光5.1 发展历史5.2 发展前景5.3 主要种类 光源是一个物理学名词,能发出一定波长范围的电磁波(包括可见光以及紫外线、红外线和X射线等不可见光)的物体。通常则指能发可见光的物体。光源可以分为自然光源(天然光源)和人造光源。太阳、打开的电灯、燃烧着的蜡烛等都是光源。在物理学中,指能发出一定波长范围的电磁波(包括可见光与紫外线、红外线、X射线等不可见光)的物体。通常指能发出可见光的发光体。但像月亮表面、桌面等依靠它们反射外来光才

文火冰糖(王文兵)的博客 1414

Unity光源

1.Spot 聚光灯2.Directional 方向光3.Point 点光源4.Area 面光源(只在烘焙状态下)

qq_51679711的博客 373

D65/TL84/A三种光源以及色温的含义

D65/TL84/A三种光源的含义

qq_42560102的博客 1万+
上一篇: 光照贴图
下一篇: 模板测试
「已注销」
博客等级 码龄17年 6粉丝 9原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值