精华内容
下载资源
问答
  • GLSL光照运用

    2012-12-05 22:33:48
    1、在GLSL中运用光照,首先需考虑的是光照强度,有了光强,有就明暗和层次感,那么光照强度如何判断?我们可以通过计算顶点的法线向量与光照向量的夹角余弦值(cosine值)来得到。所以,当法线离光照方向很接近的...

    1、在GLSL中运用光照,首先需考虑的是光照强度,有了光强,有就明暗和层次感,那么光照强度如何判断?我们可以通过计算顶点的法线向量与光照向量的夹角余弦值(cosine值)来得到。所以,当法线离光照方向很接近的情况下,将会出现亮的色调;当光照方向与法线方向的夹角很大时,渲染的地方就会很暗,换句话而言就是,夹角的cosine值影响色调的强度,Cosine值的计算可以利用下面这个公式,也即是点积公式:

     

         cos(lightDir,normal) = lightDir . normal / ( |lightDir| * |normal| )

         当法线向量和光照都被归一化,这个公式将被简化成:

         cos(lightDir,normal) = lightDir . normal


    2、所以在GLSL编程时经常会看到如下代码:

    Vec3 normal = gl_NormalMatrix * gl_Normal;

    Vec3 tnorm = normalize( normal  );

    float intensity = dot( lightdir, n );

    这里如果没有进行 normalize( normal  );那显然dot是不成行的,即得不到光的强度。

     

    3、光线向量的计算

    光线向量即从光源点发出,到顶点结束的向量,所以豪无疑问的是我们首先必须得到光源位置以及顶点位置(以观察坐标为基):

    (1)光源位置

    我们自己设置一个光源位置,比如:uniform vec3 LightPostion;在应用程序中对其赋值。也可以通过GLSL内置的光照属性结构体来获取,结构命名为gl_LightSourceParameters,大概如下:

    struct gl_LightSourceParameters {

            vec4 ambient;

            vec4 diffuse;

            vec4 specular;

            vec4 position;

            ...

    };

    uniform gl_LightSourceParameters gl_LightSource[gl_MaxLights];

    这样光源的位置为:gl_LightSource[0].postion

     

    (2)顶点位置

    经过模型视图变换的顶点位置可以这样计算:

    vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Vertex);

    注意这里是模型视图变换矩阵,不是模型视图投影变换矩阵。

     

    接下来求光线方向就非常容易了:vec3 lightVec   = normalize(LightPosition - ecPosition);

     

    4、例子1:

            varying vec3 normal;

                  

                   void main()

                   {

                                   float intensity;

                                   vec4 color;

                                   vec3 n = normalize(normal);

                                  

                                   intensity = dot(vec3(gl_LightSource[0].position),n);

                                  

                                   if (intensity > 0.95)

                                                  color = vec4(1.0,0.5,0.5,1.0);

                                   else if (intensity > 0.5)

                                                  color = vec4(0.6,0.3,0.3,1.0);

                                   else if (intensity > 0.25)

                                                  color = vec4(0.4,0.2,0.2,1.0);

                                   else

                                                  color = vec4(0.2,0.1,0.1,1.0);

                                  

                                   gl_FragColor = color;

                   }

    此处通过光强来设置不同的颜色。


    例子2:

    /// CH06-brick.vert(取自GLSL橙皮书例子)

    uniform vec3 LightPosition;

    const float SpecularContribution = 0.3;
    const float DiffuseContribution  = 1.0 - SpecularContribution;

    varying float LightIntensity;
    varying vec2  MCposition;

    void main()
    {
        vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Vertex);
        vec3 tnorm      = normalize(gl_NormalMatrix * gl_Normal);
        vec3 lightVec   = normalize(LightPosition - ecPosition);
        vec3 reflectVec = reflect(-lightVec, tnorm);
        vec3 viewVec    = normalize(-ecPosition);
        float diffuse   = max(dot(lightVec, tnorm), 0.0);
        float spec      = 0.0;

        if (diffuse > 0.0)
        {
            spec = max(dot(reflectVec, viewVec), 0.0);
            spec = pow(spec, 16.0);
        }

        LightIntensity  = DiffuseContribution * diffuse +
                          SpecularContribution * spec;

        MCposition      = gl_Vertex.xy;
        gl_Position     = ftransform();
    }

     在该顶点着色器代码中,需注意几点:

    (1)通过顶点的坐标位置(ecPosition ),及顶点的单位法线向量(tnorm),并由此计算出光线在顶点平面上的反射向量(reflectVec )。注意该函数TYPE reflect( TYPE I, TYPE N ) 它根据规范化的表面方向向量N,返回入射向量I的反射方向:result = I - 2*dot(N, I) * N

    (2)float diffuse = max(dot(lightVec, tnorm), 0.0);

    这里dot(lightVec, tnorm)实质是计算光的强度,由于dot实质是计算两向量的夹角余弦,所以当角度>90度时为负值,当处于0~90度时为正值。

     max(dot(lightVec, tnorm), 0.0);的意思也即当光线方向的逆方向(注意lightVec的定义)与法向量的夹角大于90度时,取0值(即就是说完全没有漫射光了,比如想像一下点法向量朝Z轴正上,而光线方向是(1, 1, 1) )。

    (3)if (diffuse > 0.0) 即如果存在漫射光,那必然是有反射光的。

            spec = max(dot(reflectVec, viewVec), 0.0);
            spec = pow(spec, 16.0);
    这里dot(reflectVec, viewVec)表示反射光强,前面讲过很多了。

    pow也是一个GLSL的内置函数,TYPE pow( TYPE x, TYPE y )

    表x的y次方。注意spec是个float型数,所以必须为16.0

    此处用16次方是随意的,你可以用任意一个数,它只是为了使反射光更加明显,当光线向量与点法微量非常接近的时候,不至于看不见。

     

    /// CH06-brick.frag(取自GLSL橙皮书例子)

    uniform vec3  BrickColor, MortarColor;
    uniform vec2  BrickSize;
    uniform vec2  BrickPct;

    varying vec2  MCposition;
    varying float LightIntensity;

    void main()
    {
        vec3  color;
        vec2  position, useBrick;
        
        position = MCposition / BrickSize;

        if (fract(position.y * 0.5) > 0.5)
            position.x += 0.5;

        position = fract(position);

        useBrick = step(position, BrickPct);

        color  = mix(MortarColor, BrickColor, useBrick.x * useBrick.y);
        color *= LightIntensity;
        gl_FragColor = vec4(color, 1.0);
    }

    展开全文
  • glsl光照计算

    2009-08-09 20:54:00
    function provided by GLSL. intensity = dot(lightDir, gl_Normal); The only thing that's left to do in the vertex shader is to transform the vertex coordinates. The complete code for the shader is...

     

    The first version preseted in here computes an intensity per vertex. Then the fragment shader uses the vertex interpolated intensity to compute a tone for the fragment. The vertex shader must therefore declare a varying variable to store the intensity. The fragment shader must declare the same variable, also using the varying qualifier, to receive the properly interpolated value for the intensity.

    The light direction could be defined in the vertex shader as a local variable or as a constant, however having it as a uniform variable provides more freedom since it can be set arbitrarily on the OpenGL application. The light's direction variable will be defined in the shader as

    	uniform vec3 lightDir;//一致变量,从opengl程序传过来
    

    For now, lets assume that the light's direction is defined in world space.

    The vertex shader has access to the normals, as specified in the OpenGL application, through the attribute variable gl_Normal. This is the normal as defined in the OpenGL application with the glNormal function, hence in model local space.

    If no rotations or scales are performed on the model in the OpenGL application, then the normal defined in world space, provided to the vertex shader as gl_Normal, coincides with the normal defined in the local space. The normal is a direction and therefore it is not affected by translations.

    Because both the normal and the light's direction are specified in the same space, the vertex shader can jump directly to the cosine computation between the light's direction, i.e. lightDir, and the normal. The cosine can be computed using the following formula

    	cos(lightDir,normal) = lightDir . normal / ( |lightDir| * |normal| )//光照方向和顶点向量的夹角
    
    where "." is the inner product, aka as the dot product. This can be simplified if both the normal and lightDir are normalized, i.e.
    	| normal | = 1
    	| lightDir | = 1
    
    Hence if these two conditions are guaranteed the computation for the cosine can be simplified to
    	cos(lightDir,normal) = lightDir . normal 
    
    Since the variable lightDir is supplied by the OpenGL application we can assume that it arrives at the shader already normalized. It would be a waste of time having to normalize it for every vertex, instead of performing it only when the lightDir changes. Also it is reasonable to expect that the normals from the OpenGL application are normalized.

    Therefore the cosine, which we will store in a variable named intensity, can be computed with the dot function provided by GLSL.

    	intensity = dot(lightDir, gl_Normal);
    

    The only thing that's left to do in the vertex shader is to transform the vertex coordinates. The complete code for the shader is as follows:


    	uniform vec3 lightDir;
    	
    	varying float intensity;
    	
    	void main()
    	{
    		vec3 ld;
    		
    		intensity = dot(lightDir,gl_Normal);//得到光照方向与顶点向量的余弦值,并传到片元着色器中
    		
    		gl_Position = ftransform();
    	} 
    

    Now, in the fragment shader, all that's left to do is to define a color for the fragment based on the intensity. The intensity must be passed on to the fragment shader, since it is the fragment shader that is responsible for setting the colors for fragments. As mentioned before, the intensity will be defined as a varying variable on both shaders, hence it must be written in the vertex shader for the fragment shader to read it.

    The color can be computed in the fragment shader as follows:


    	vec4 color;
    	
    	if (intensity > 0.95)
    		color = vec4(1.0,0.5,0.5,1.0);
    	else if (intensity > 0.5)
    		color = vec4(0.6,0.3,0.3,1.0);
    	else if (intensity > 0.25)
    		color = vec4(0.4,0.2,0.2,1.0);
    	else
    		color = vec4(0.2,0.1,0.1,1.0);
    
    

    As can be seen from the code above, the brightest color is used when the cosine is larger than 0.95 and the darker color is used for cosines smaller than 0.25. All there is left to do in the fragment shader is to set the gl_FragColor based on the color. The code for the fragment shader is:


    	varying float intensity;
    	
    	void main()
    	{
    		vec4 color;
    		
    		if (intensity > 0.95)
    			color = vec4(1.0,0.5,0.5,1.0);
    		else if (intensity > 0.5)
    			color = vec4(0.6,0.3,0.3,1.0);
    		else if (intensity > 0.25)
    			color = vec4(0.4,0.2,0.2,1.0);
    		else
    			color = vec4(0.2,0.1,0.1,1.0);
    	
    		gl_FragColor = color;
    	}
    

    The following image shows the end result, and it doesn't look very nice does it? The main problem is that we're interpolating the intensity. This is not the same as computing the intensity with the proper normal for the fragment. Go on to the next section to see toon shading done properly!
    //--------被充
    这个例子很简单,就是在程序中设置光照的方向后,如果光照的方向与顶点向量的方向的余弦在某个范围内,设置不同的艳色

    转载于:https://www.cnblogs.com/lizhengjin/archive/2009/08/09/1542407.html

    展开全文
  • 冯氏光照模型 首先,在现实世界的光照是极其复杂的,而且会受到诸多因素的影响,这是我们有限的计算能力所无法模拟的。因此OpenGL的光照使用的是简化的模型,对现实的情况进行近似,这样处理起来会更容易一些。这些...

    冯氏光照模型

    首先,在现实世界的光照是极其复杂的,而且会受到诸多因素的影响,这是我们有限的计算能力所无法模拟的。因此OpenGL的光照使用的是简化的模型,对现实的情况进行近似,这样处理起来会更容易一些。这些光照模型都是基于我们对光的物理特性的理解。其中一个模型被称为冯氏光照模型(Phong Lighting Model)。冯氏光照模型的主要结构由3个元素组成:环境(Ambient)光照、漫反射(Diffuse)光照和镜面(Specular)光照。 下面我们用示例图来看一下这几种光照:

    环境(Ambient)光照:即使在最黑暗的正常环境下,世界上依然会存在一些光亮,也就是说物体几乎永远不会是完全黑暗的。环境光照一般是由室外的太阳通过各种折射来让我们看到,这样的光是没有任何起点,没有方向的光。主要通过设置物体颜色来获取。 漫反射(Diffuse)光照:模拟的是光源对物体方向的影响,一个物体面向光源的时候,朝向光的那个面会亮一点,物体背面和其他面会暗一点。 镜面(Specular)光照:模拟的是有光泽物体上面出现的亮点。镜面光照的颜色相比于物体的颜色会更倾向于光的颜色。

    光照基础

    OpenGL在处理光照时把光照系统分为三部分:分别是光源、材质和光照环境。光源就是光的来源,可以是前面所说的太阳或者电灯,蜡烛等。材质是指接受光照的各种物体的表面,由于物体如何反射光线只由物体表面决定,材质特点就决定了物体反射光线的特点。光照环境是指一些额外的参数,它们将影响最终的光照画面,比如一些光线经过多次反射后,已经无法分清它究竟是由哪个光源发出,这时,指定一个环境亮度参数,可以使最后形成的画面更接近于真实情况。

    在物理学中,光线如果射入理想的光滑平面,则反射后的光线是很规则的(这样的反射称为镜面反射)。光线如果射入粗糙的、不光滑的平面,则反射后的光线是杂乱的(这样的反射称为漫反射)。现实生活中的物体在反射光线时,并不是绝对的镜面反射或漫反射,但可以看成是这两种反射的叠加。对于光源发出的光线,可以分别设置其经过镜面反射和漫反射后的光线强度。对于被光线照射的材质,也可以分别设置光线经过镜面反射和漫反射后的光线强度。这些因素综合起来,就形成了最终的光照效果。

    光照特性 1.发射光:由物体自身发光 2.环境光:就是在环境中充分散射的光,而且无法分辨光的方向 3.漫反射光:光线来自某个方向,但是在物体上各个方向反射 4.镜面高光:光线来自一个特定的方向,然后在物体表面上以一个特定的方向反射出去

    材质属性 1.泛射材质:光线直射,反射率较高 2.漫反射材质:需要考虑光的入射角和反射角的 3.镜面反射材质:斑点 4.发射材质:物体本身就可以发光的材质

    光照计算

    1.环境光的计算

    环境光是不来自任何特定方向的光,在整个场景中经典光照模型把它当成一个常量,组成一个合适的第一近似值来缩放场景中的光照部分。 环境光 = 光源的环境光颜色 * 物体的材质颜色

    varying vec3 objectColor;
    void main()
    {
      //⾄至少有%10的光找到物体所有⾯面
      float ambientStrength = 0.1;
      //环境光颜⾊色
      vec3 ambient = ambientStrength * lightColor;
      //最终颜⾊色 = 环境光颜⾊色 * 物体颜⾊色
      vec3 result = ambient * objectColor;
      gl_FragColor = vec4(result, 1.0);
    }
    复制代码

    2.发射光的计算

    如果这个物体本身就是有颜色的,比如说夜明珠,那么这个时候这个光就是这个物体材质的颜色 发射颜色 = 物体的反射材质颜色

    3.漫反射光的计算

    漫反射光是散射在各个方向上的均匀的表面特定光源,漫反射光依赖于表面法线方向和光源方向来计算,但是没有包含视线方向,它同样依赖于表面的颜色。 首先来看一下环境光和漫反射光的比较:

    可以看到环境光下的苹果是没有阴面和阳面的,看到的苹果感觉不够逼真,而漫反射光加深了苹果的真实度,模拟出了在现实生活中的真实环境。
    上图是当漫反射光照射到物体表面时:其中N表示法向量,L表示光源,法向量N和光源L之间的夹角决定了光照射的面积。夹角越大照射面积越大。

    光线照射到物体表面,决定了物体表面的光照强度,光照强度是光本身强度和光线与物体表面法线夹角cos的乘积

    有效光的光照方向是与物体表面法线夹角在0~90度之间的。

    漫反射颜色 = 光源的漫反射光颜色 × 物体的漫反射材质颜色 × 漫反射因子

    漫反射因子DiffuseFactor 是光线与顶点法线向量的点积 DiffuseFactor = max(0, dot(N, L))

    uniform vec3 lightColor;    //光源色
    uniform vec3 lightPo;     //光源位置
    uniform vec3 objectColor; //物体⾊色
    uniform vec3 viewPo;  //物体位置
    varying vec3 outNormal; //传⼊当前顶点平面的法向量
    
    //确保法线为单位向量量
    vec3 norm = normalize(outNormal); 
    //顶点指向光源 单位向量量
    vec3 lightDir = normalize(lightPo - FragPo);
     //得到两向量量的cos值 ⼩小于0则则为0
    float diff = max(dot(norm, lightDir),0.0); 
    //得到漫反射收的光源向量量
    vec3 diffuse = diff * lightColor;
    vec3 result  = diffuse * ojbectColor;
    gl_FragColor = vec4(result,1.0);
    复制代码

    4.镜面光照计算 镜面光是由表面直接反射的高亮光,这个高亮光就像镜子一样跟表面材质多少有关。

    其中:N表示平面法线,R表示反射光线,@表示视点与反射光的夹角

    镜面反射颜色 = 光源的镜面光颜色 × 物体的镜面材质颜色 × 镜面反射因子

    镜面反射因子SpecularFactor = power(max(0,dot(N,R)),shininess)

    dot(N,R):H,R的点积几何意义:平⽅线与法线夹角的cos值

    shiniess : ⾼光的反光度/发光值;(值越大反射度越强)

    一个物体的发光值越高,反射光的能力越强,散射得越少,高光点越小。在下面的图片里,你会看到不同发光值对视觉(效果)的影响:

    一般我们不希望镜面成分过于显眼,所以我们通常把shiniess指数设置为32.

    镜面光的GLSL实现代码:

    //镜⾯面强度
    float specularStrength = 0.5;
    //顶点指向观察点的单位向量量
    vec3 viewDir = normalize(viewPo - FragPo);
    //求得光线 在 顶点的反射线(传⼊入光源指向顶点的向量量)
    vec3 reflectDir = reflect(-lightDir ,outNormal);
    // 求得夹⻆角cos值 取256次幂 注意 pow(float,float)函数参数类型 float spec = pow(max(dot(viewDir, reflectDir),0.0),256.0);
    vec3 specular = specularStrength * spec * lightColor;
    复制代码

    我们都知道光的传播是会衰减的,所以光照颜色的公式可总结为:

    光照颜色 =(环境颜色 + 漫反射颜色 + 镜⾯反射颜色)* 衰减因子

    衰减因子 = 1.0/(距离衰减常量 + 线性衰减常量 * 距离 + ⼆次衰减常量 * 距离的平⽅)

    //距离衰减常量量
    float constantPara = 1.0f;
    //线性衰减常量量
    float linearPara = 0.09f;
    //⼆二次衰减因⼦子
    float quadraticPara = 0.032f;
    //距离
    float LFDistance = length(lightPo - FragPo);
    //衰减因⼦子
    float lightWeakPara = 1.0/(constantPara + linearPara * LFDistance + quadraticPara * (LFDistance*LFDistance));
    复制代码

    距离衰减常量,线性衰减常量和⼆次衰减常量均为常量值.

    环境光,漫反射光和镜面光的强度都会受距离的增大⽽衰减,只有发射光和全局环境光的强度不会受影响.

    聚光灯夹角cos值 = power(max(0,dot(单位光源位置,单位光线向量)),聚光灯指数);

    单位光线向量是从光源指向顶点的单位向量 聚光灯指数,表示聚光灯的亮度程度 公式解读:单位光源位置 * 单位光线向量点积的聚光灯指数次⽅。

    聚光灯因子 = clamp((外环的聚光灯角度cos值 - 当前顶点的聚光灯角度cos值)/ (外环的聚光灯角度cos值- 内环聚光灯的角度的cos值),0,1);

    //聚光灯过渡计算
    //(⼀些复杂的计算操作 应该让CPU做,提⾼效率,不变的量也建议外部传输,避免重复计算)
    //内锥角cos值
    float inCutOff = cos(radians(10.0f)); 
    //外锥角cos值
    float outCutOff = cos(radians(15.0f)); //聚光朝向
    vec3 spotDir = vec3(-1.2f,-1.0f,-2.0f);
    //光源指向物体的向量和聚光朝向的 cos值
    float theta = dot(lightDir ,normalize(-spotDir)); 
    //内外锥⻆角cos差值
    float epsilon = inCutOff - outCutOff;
    
    //clamp(a,b,c);若b<a<c 则函数返回值为a 
    //若不是,则返回值最小为b ,最大为c
    // (theta - outCutOff)/epsilon 若theta的角度⼩于内锥角 则其值 >=1 
    //若theta的⻆度大于外锥角,则其值<=0 这样光线就在内外锥角之间平滑变化.
    float intensity = clamp((theta - outCutOff)/epsilon, 0.0,1.0)
    复制代码

    光照颜色的最终公式为:

    光照颜色 = 发射颜色 + 全局环境颜色 + (环境颜色 + 漫反射颜色 + 镜⾯反射颜色) * 聚光灯效果 * 衰减因子

    下面通过一个GLKit绘制金字塔案例来看一下光照的使用:

    如果把效果图里的三角形的顶点全部画到平面上来,如下图所示:

    1.设置OpenGL ES

    @property(nonatomic,strong)EAGLContext *mContext;
    //基本Effect 绘图
    @property(nonatomic,strong)GLKBaseEffect *baseEffect;
    //额外Effect 辅助线段
    @property(nonatomic,strong)GLKBaseEffect *extraEffect;
    //顶点缓存区 (顶点,颜色,纹理, 法线...)
    @property(nonatomic,strong)AGLKVertexAttribArrayBuffer *vertexBuffer;
    //法线位置缓存区(法线辅助线段也有顶点)
    @property(nonatomic,strong)AGLKVertexAttribArrayBuffer *extraBuffer;
    //是否绘制法线
    @property(nonatomic,assign)BOOL shouldDrawNormals;
    //中心点的高 默认在(0,0,0)
    @property(nonatomic,assign) GLfloat centexVertexHeight;
    
    
    
    {
        //三角形-8面
        SceneTriangle triangles[NUM_FACES];
    }
    复制代码

    设置GLKitView并设置上下文

    //1.新建OpenGL ES 上下文
    self.mContext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
        
    //2.设置GLKView
    GLKView *view = (GLKView *)self.view;
    view.context = self.mContext;
    view.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    [EAGLContext setCurrentContext:self.mContext];
    复制代码

    2. 设置金字塔Effect

       //1.金字塔Effect
        self.baseEffect = [[GLKBaseEffect alloc]init];
        self.baseEffect.light0.enabled = GL_TRUE;
        //光的漫射部分 GLKVector4Make(R,G,B,A)
        self.baseEffect.light0.diffuseColor = GLKVector4Make(0.7f, 0.7f, 0.7, 1.0f);
        //世界坐标中的光的位置。
        self.baseEffect.light0.position = GLKVector4Make(1.0f, 1.0f, 0.5f, 0.0f);
        
        //2.法线Effect
        self.extraEffect = [[GLKBaseEffect alloc]init];
        self.extraEffect.useConstantColor = GL_TRUE;
        
        //3.调整模型矩阵,更好的观察
        //可以尝试不执行这段代码,改为false
        if (true) {
            
            //围绕x轴旋转-60度
            //返回一个4x4矩阵进行绕任意矢量旋转
            GLKMatrix4 modelViewMatrix = GLKMatrix4MakeRotation(GLKMathDegreesToRadians(-60.0f), 1.0f, 0.0f, 0.0f);
            
            //围绕z轴,旋转-30度
            modelViewMatrix = GLKMatrix4Rotate(modelViewMatrix,GLKMathDegreesToRadians(-30.0f), 0.0f, 0.0f, 1.0f);
            
            //围绕Z方向,移动0.25f
            modelViewMatrix = GLKMatrix4Translate(modelViewMatrix, 0.0f, 0.0f, 0.25f);
            
            //设置baseEffect,extraEffect 模型矩阵
            self.baseEffect.transform.modelviewMatrix = modelViewMatrix;
            self.extraEffect.transform.modelviewMatrix = modelViewMatrix;    
        }
    复制代码

    3.设置顶点

       //确定图形的8个面
        triangles[0] = SceneTriangleMake(vertexA, vertexB, vertexD);
        triangles[1] = SceneTriangleMake(vertexB, vertexC, vertexF);
        triangles[2] = SceneTriangleMake(vertexD, vertexB, vertexE);
        triangles[3] = SceneTriangleMake(vertexE, vertexB, vertexF);
        triangles[4] = SceneTriangleMake(vertexD, vertexE, vertexH);
        triangles[5] = SceneTriangleMake(vertexE, vertexF, vertexH);
        triangles[6] = SceneTriangleMake(vertexG, vertexD, vertexH);
        triangles[7] = SceneTriangleMake(vertexH, vertexF, vertexI);
        
        //初始化缓存区
        self.vertexBuffer = [[AGLKVertexAttribArrayBuffer alloc]initWithAttribStride:sizeof(SceneVertex) numberOfVertices:sizeof(triangles)/sizeof(SceneVertex) bytes:triangles usage:GL_DYNAMIC_DRAW];
        
        self.extraBuffer = [[AGLKVertexAttribArrayBuffer alloc]initWithAttribStride:sizeof(SceneVertex) numberOfVertices:0 bytes:NULL usage:GL_DYNAMIC_DRAW];
    
        self.centexVertexHeight = 0.0f;
    复制代码

    4.开始绘制

    -(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
    {
        //1.
        glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);
        
        //2.
        [self.baseEffect prepareToDraw];
        //准备绘制顶点数据
        [self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribPosition numberOfCoordinates:3 attribOffset:offsetof(SceneVertex,position)shouldEnable:YES];
        //准备绘制光照数据
        [self.vertexBuffer prepareToDrawWithAttrib:GLKVertexAttribNormal numberOfCoordinates:3 attribOffset:offsetof(SceneVertex, normal) shouldEnable:YES];
        [self.vertexBuffer drawArrayWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:sizeof(triangles)/sizeof(SceneVertex)];
        
        //3.是否要绘制光照法线
        if (self.shouldDrawNormals) {
            [self drawNormals];
        }
    }
    复制代码

    绘制法线

    //绘制法线
    -(void)drawNormals
    {
        
        GLKVector3 normalLineVertices[NUM_LINE_VERTS];
        
        //1.以每个顶点的坐标为起点,顶点坐标加上法向量的偏移值作为终点,更新法线显示数组
        //参数1: 三角形数组
        //参数2:光源位置
        //参数3:法线显示的顶点数组
        SceneTrianglesNormalLinesUpdate(triangles, GLKVector3MakeWithArray(self.baseEffect.light0.position.v), normalLineVertices);
        
        //2.为extraBuffer重新开辟空间
        [self.extraBuffer reinitWithAttribStride:sizeof(GLKVector3) numberOfVertices:NUM_LINE_VERTS bytes:normalLineVertices];
        
        //3.准备绘制数据
        [self.extraBuffer prepareToDrawWithAttrib:GLKVertexAttribPosition numberOfCoordinates:3 attribOffset:0 shouldEnable:YES];
        
        //4.修改extraEffect
        //法线
        /*
         指示是否使用常量颜色的布尔值。
         如果该值设置为gl_true,然后存储在设置属性的值为每个顶点的颜色值。如果该值设置为gl_false,那么你的应用将使glkvertexattribcolor属性提供每顶点颜色数据。默认值是gl_false。
         */
        self.extraEffect.useConstantColor = GL_TRUE;
        //设置光源颜色为绿色,画顶点法线
        self.extraEffect.constantColor = GLKVector4Make(0.0f, 1.0f, 0.0f, 1.0f);
        //准备绘制-绿色的法线
        [self.extraEffect prepareToDraw];
        //绘制线段
        [self.extraBuffer drawArrayWithMode:GL_LINES startVertexIndex:0 numberOfVertices:NUM_NORMAL_LINE_VERTS];
        
        //设置光源颜色为黄色,并且画光源线
        //Red+Green =Yellow
        self.extraEffect.constantColor = GLKVector4Make(1.0f, 1.0f, 0.0f, 1.0f);
        
        //准备绘制-黄色的光源方向线
        [self.extraEffect prepareToDraw];
        
        //(NUM_LINE_VERTS - NUM_NORMAL_LINE_VERTS) = 2 .2点确定一条线
        [self.extraBuffer drawArrayWithMode:GL_LINES startVertexIndex:NUM_NORMAL_LINE_VERTS numberOfVertices:2];
    }
    
    //更新法向量
    -(void)updateNormals
    {
        //更新每个点的平面法向量
        SceneTrianglesUpdateFaceNormals(triangles);
        [self.vertexBuffer reinitWithAttribStride:sizeof(SceneVertex) numberOfVertices:sizeof(triangles)/sizeof(SceneVertex) bytes:triangles];
    }
    复制代码

    更新中心顶点

    - (IBAction)changeCenterVertexHeight:(UISlider *)sender {
      
        self.centexVertexHeight = sender.value;
    }
    
    -(void)setCentexVertexHeight:(GLfloat)centexVertexHeight
    {
        _centexVertexHeight = centexVertexHeight;
        
        //更新顶点 E
        SceneVertex newVertexE = vertexE;
        newVertexE.position.z = _centexVertexHeight;
        
        triangles[2] = SceneTriangleMake(vertexD, vertexB, newVertexE);
        triangles[3] = SceneTriangleMake(newVertexE, vertexB, vertexF);
        triangles[4] = SceneTriangleMake(vertexD, newVertexE, vertexH);
        triangles[5] = SceneTriangleMake(newVertexE, vertexF, vertexH);
        
        //更新法线
        [self updateNormals];
    }
    复制代码

    在实际的开发中,我们在绘图时是不会把法线绘制在图形中的,所以绘制法线部分代码可以忽略。

    转载于:https://juejin.im/post/5d09f5cf6fb9a07eaa227fd3

    展开全文
  • CSharpGL(39)GLSL光照示例:鼠标拖动太阳(光源)观察平行光的漫反射和镜面反射效果 开始 一图抵千言。首先来看鼠标拖动太阳(光源)的情形。 然后是鼠标拖拽旋转模型的情形。 然后我们移动摄像机来从不同的...

    CSharpGL(39)GLSL光照示例:鼠标拖动太阳(光源)观察平行光的漫反射和镜面反射效果

    开始

    一图抵千言。首先来看鼠标拖动太阳(光源)的情形。

    然后是鼠标拖拽旋转模型的情形。

    然后我们移动摄像机来从不同的角度看看。

    现在太阳(光源)跑到比较远的位置去了,我们再移动它试试看。

    本文就介绍平行光下是如何实现漫反射和镜面反射的。

    本文shader核心部分来自红宝书第八版。

    光照

    只需记住一点,用GLSL实现光照效果时,都是根据顶点的位置、法线方向、光源位置(方向)、摄像机位置等等这些数据,根据物理学的反射规则计算出来的。当然,为了兼顾效果和效率,可能会对物理规则做一些简化处理。

    Vertex shader

    先看vertex shader。除了传递顶点的位置、颜色外,这里新增了传递法线(normal)的代码。顶点的变换可以通过mvp矩阵完成,而法线的变换则有所不同(说来话长,这里不要深究),所以单独提供一个normalMatrix供法线使用。

     1 #version 330 core
     2 
     3 uniform mat4 mvpMatrix;
     4 uniform mat3 normalMatrix;// normal matrix is transpose(inverse(model matrix))
     5 
     6 in vec3 inPosition;
     7 in vec3 inColor;
     8 in vec3 inNormal;
     9 
    10 out vec3 passNormal;
    11 out vec3 passColor;
    12 
    13 void main()
    14 {
    15     passNormal = normalize(normalMatrix * inNormal);
    16     passColor = inColor;
    17     gl_Position = mvpMatrix * vec4(inPosition, 1.0);
    18 }

    Fragment shader

    这里是计算光照的地方。详情见注释。

     1 #version 330 core
     2 
     3 uniform vec3 ambientLight;// 环境光
     4 uniform vec3 directionalLightColor;
     5 uniform vec3 directionalLightDirection;
     6 uniform vec3 halfVector;
     7 uniform float shininess;
     8 uniform float strength;
     9 
    10 in vec3 passNormal;
    11 in vec3 passColor;
    12 
    13 out vec4 outColor;
    14 
    15 void main()
    16 {
    17     // 根据光源方向与法线方向的夹角计算此处的漫反射的强度
    18     float diffuse = max(0.0, dot(passNormal, directionalLightDirection));
    19     // 计算此处的镜面反射的强度
    20     float specular = max(0.0, dot(passNormal, halfVector));
    21     
    22     if (diffuse == 0.0) { specular = 0.0; }// 若光源没有照射到此处,自然也不应该有镜面反射效果
    23     else { specular = pow(specular, shininess); }// 指数式的剧烈变化,就是产生镜面高光的原理
    24     
    25     vec3 scatteredLight = ambientLight + directionalLightColor * diffuse;// 漫反射光+环境光
    26     vec3 reflectedLight = directionalLightColor * specular * strength;// 镜面反射光
    27 
    28     vec3 rgb = min(passColor * scatteredLight + reflectedLight, vec3(1.0));// 最后的颜色
    29     outColor = vec4(rgb, 1.0);// 搞定
    30

    渲染器

    shader做好了,下面写一个渲染器。关于渲染器的详细介绍可参看(CSharpGL(34)以从零编写一个KleinBottle渲染器为例学习如何使用CSharpGL)由于制作光照效果需要模型自带法线值,而我手里的模型只有这个Teapot是有法线值的,又仅仅是个例子,就写死了用Teapot了。

     1     class DirectonalLightRenderer : PickableRenderer
     2     {
     3         public vec3 AmbientLightColor { get; set; }
     4         public vec3 DirectionalLightDirection { get; set; }
     5         public vec3 DirectionalLightColor { get; set; }
     6         //public vec3 HalfVector { get; set; }
     7         public float Shininess { get; set; }
     8         public float Strength { get; set; }
     9 
    10         public static DirectonalLightRenderer Create()
    11         {
    12             var model = new Teapot();
    13             var shaderCodes = new ShaderCode[2];
    14             shaderCodes[0] = new ShaderCode(File.ReadAllText(@"shaders\DirectionalLight.vert"), ShaderType.VertexShader);
    15             shaderCodes[1] = new ShaderCode(File.ReadAllText(@"shaders\DirectionalLight.frag"), ShaderType.FragmentShader);
    16             var map = new AttributeMap();
    17             map.Add("inPosition", Teapot.strPosition);
    18             map.Add("inColor", Teapot.strColor);
    19             map.Add("inNormal", Teapot.strNormal);
    20 
    21             var renderer = new DirectonalLightRenderer(model, shaderCodes, map, Teapot.strPosition);
    22             renderer.ModelSize = model.Size;
    23             return renderer;
    24         }
    25 
    26         private DirectonalLightRenderer(IBufferable model, ShaderCode[] shaderCodes,
    27             AttributeMap attributeMap, string positionNameInIBufferable,
    28             params GLState[] switches)
    29             : base(model, shaderCodes, attributeMap, positionNameInIBufferable, switches)
    30         {
    31             this.AmbientLightColor = new vec3(0.2f);
    32             this.DirectionalLightDirection = new vec3(1);
    33             this.DirectionalLightColor = new vec3(1);
    34             //this.HalfVector = new vec3(1);
    35             this.Shininess = 10.0f;
    36             this.Strength = 1.0f;
    37         }
    38 
    39         protected override void DoRender(RenderEventArgs arg)
    40         {
    41             this.SetUniform("ambientLight", this.AmbientLightColor);
    42             this.SetUniform("directionalLightColor", this.DirectionalLightColor);
    43             this.SetUniform("directionalLightDirection", this.DirectionalLightDirection.normalize());
    44             this.SetUniform("halfVector", this.DirectionalLightDirection.normalize());
    45             //this.SetUniform("halfVector", this.HalfVector.normalize());
    46             this.SetUniform("shininess", this.Shininess);
    47             this.SetUniform("strength", this.Strength);
    48 
    49             mat4 projection = arg.Camera.GetProjectionMatrix();
    50             mat4 view = arg.Camera.GetViewMatrix();
    51             mat4 model = this.GetModelMatrix().Value;
    52             this.SetUniform("mvpMatrix", projection * view * model);
    53             this.SetUniform("normalMatrix", glm.transpose(glm.inverse(model)).to_mat3());
    54 
    55             base.DoRender(arg);
    56         }
    57     }
    DirectonalLightRenderer

    这样其实就可以看到效果了,还可以通过属性面板控制光源的参数。

    但是手动输入数值很不爽啊,没有一点点随心所欲的顺畅。于是后续的折腾就开始了。让我来画一个真正的太阳,然后通过鼠标拖动太阳,实时更新光源的位置(方向),看到本文开始的效果。

    太阳(光源)

    画太阳

    其实太阳模型早就做过了,本质上就是利用一个noise方法模拟太阳表面的活动。外围辐射效果什么的,我先拉倒吧。

    拖动太阳

    把太阳放在那里很容易,如何用鼠标移动呢?

    原理在(CSharpGL(20)用unProject和Project实现鼠标拖拽图元)已经整理出来了。只不过当时是单独修改模型内部的顶点位置,而现在需要整体移动模型,即修改模型的(RendererBase.WorldPosition)属性。

    我的思路如下:假设有一个点在原点position = new vec3(0,0,0),我们像之前一样计算它在平移之后的位置newPosition,这是模型本身的变化,然后只需分别通过RendererBase.GetModelMatrix()的变换,就变成了在World Space里的变化,这个差别就是模型的位移。代码如下。

     1         void IMouseHandler.canvas_MouseMove(object sender, MouseEventArgs e)
     2         {
     3             if (mouseDownFlag && ((e.Button & this.lastBindingMouseButtons) != MouseButtons.None))
     4             {
     5                 Point location = new Point(e.X, this.canvas.ClientRectangle.Height - e.Y - 1);
     6                 Point differenceOnScreen = new Point(location.X - this._lastPosition.X, location.Y - this._lastPosition.Y);
     7                 mat4 model = this.renderer.GetModelMatrix().Value;
     8                 mat4 view = this.camera.GetViewMatrix();
     9                 mat4 projection = this.camera.GetProjectionMatrix();
    10                 vec4 viewport;
    11                 {
    12                     int[] result = OpenGL.GetViewport();
    13                     viewport = new vec4(result[0], result[1], result[2], result[3]);
    14                 }
    15                 var position = new vec3(0.0f);// imangine we have a point at (0, 0, 0).
    16                 vec3 windowPos = glm.project(position, view * model, projection, viewport);
    17                 var newWindowPos = new vec3(windowPos.x + differenceOnScreen.X, windowPos.y + differenceOnScreen.Y, windowPos.z);
    18                 vec3 newPosition = glm.unProject(newWindowPos, view * model, projection, viewport);
    19                 var worldPosition = new vec3(model * new vec4(position, 1.0f));
    20                 var newWorldPosition = new vec3(model * new vec4(newPosition, 1.0f));
    21                 this.renderer.WorldPosition += newWorldPosition - worldPosition;
    22 
    23                 this._lastPosition = location;
    24             }
    25         }

    这样说似乎也不能彻底解释清楚,因为还需要先理解OpenGL里坐标变换的问题,这个问题可以参看(CSharpGL(27)讲讲清楚OpenGL坐标变换

    总结

    整完收工。

     

    展开全文
  • OpenGL进阶(十三) - GLSL光照(Lighting)

    千次阅读 2013-08-23 21:09:06
     在上一篇文章中,我们介绍了简单的Shading,同时提出了一个光照模型,模拟了一个点光源,但是,关于光的故事还没有结束...   今天要学习的是方向光源(Directional Light),聚光源(),多光源,影子,面光源。...
  • [GLSL] 光照

    2014-04-16 09:56:58
    本教程将从方向光讲起,首先我们将使用GLSL来模仿OpenGL中的光。 我们将向shader中逐渐添加环境光、散射光和高光效果。 后面的教程中我们将使用逐像素光照以获得更好的效果。 接下来我们将实现逐像素的点...
  • GLSL_Lighting.zip

    2017-01-15 00:02:39
    GLSL光照示例。
  • GLSL逐顶点的光照

    千次阅读 2018-06-11 20:56:51
    1. 【GLSL教程】(六)逐顶点的光照 2. 【GLSL教程】(七)逐像素的光照 引言 在OpenGL中有三种类型的光:方向光(directional)、点光(point)、聚光(spotlight)。 逐顶点光照的方向光,向shader中逐渐...
  • //光照属性 void main() { vec3 ecPosition = vec3(gl_ModelViewMatrix * gl_Vertex);//将物体进行模型视图中的一些变换(旋转 缩放 平移) vec3 tnorm = normalize(gl_NormalMatrix * gl_Norma
  • GLSL 中的光照计算

    2019-09-29 03:26:33
    基于GLSL的实现(所有光照和材质参数在实际应用时,应该从常量改为uniform,由外部进行控制): // 在顶点着色器中处理光照计算(环境光+漫射光+镜面反射) const char* ccPositionTextureColorForLight1_v = " \ ...
  • 文件夹中包括opengl配置库,把lib dll 头文件配置好 程序即可运行。
  • OpenGL 4.0 GLSL 采用平行光照模型

    千次阅读 2014-02-12 10:02:53
    在点光源光照模型中,一个重要的向量就是从物体表面到光源的向量(s)。但如果光源处于无穷远处,那么物体表面所有点到光源的向量可以近似看作是平行的,这样,我们可以用一个方向向量代替原来的光源向量s(比如太阳...
  • 现代OpenGL+Qt写的一个示例程序,主要用GLSL函数实现Phong光照模型,此外可以通过鼠标控制模型的旋转和缩放。对应博文现代OpenGL+Qt学习笔记之七:Phong光照及在GLSL中使用函数。
  • OpenGL中GLSL渲染茶壶光照完整程序

    千次阅读 2017-01-04 22:21:01
    //1、查看显卡、GLSL和OpenGL的信息 const GLubyte *vendor = glGetString(GL_VENDOR); const GLubyte *renderer = glGetString(GL_RENDERER); const GLubyte *version = glGetString(GL_VERSION); const ...
  • 使用GLSL实现对光照的模拟(一)

    千次阅读 2014-04-12 09:27:25
    OpenGL1.1是很老的OpenGL标准了,即使这样,对于光照也是提供了很好的...互联网上搜索GLSL+光照,相应的文章也是非常的多。有些直接成了我参考的蓝本。不管怎么样,花了大概一周的时间在以前的基础上使用Qt + OpenGL
  • GLSL逐个像素光照问题

    2012-04-24 15:37:51
    作用是使用逐像素光照,但是在实际的应用程序中却出现了错误的效果。 使用逐个像素光照应该出现如第一图均匀的镜面高光。 但在我的程序中没有出现镜面高光,旋转后发现只有壶把和壶嘴上有,这是为什么? ...
  • GLSL 逐顶点的光照

    千次阅读 2012-08-16 11:04:49
    本教程将从方向光讲起,首先我们将使用GLSL来模仿OpenGL中的光。 我们将向shader中逐渐添加环境光、散射光和高光效果。 后面的教程中我们将使用逐像素光照以获得更好的效果。 接下来我们将实现逐像素的点光...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,245
精华内容 1,298
关键字:

glsl光照