zoukankan      html  css  js  c++  java
  • [GLSL]着色器周记01——真实光照

    我决定开个新坑了。以后每周五更新。
    这是GLSL的学习周记!
    GLSL就是OPENGL SHADER LANGUAGE的简称,就是着色器语言。
    着色器是一种交给显卡运行的小程序,这种小程序可以用GLSL来写,写好后交给OPENGL编译,就可以在显卡上运行了。
     那么问题来了!为什么要给显卡运行呢?
    显卡是一种特殊的处理器,有核心,有寄存器,还有内存,不过对比CPU,最大的特点就是显卡的核心更多。多多少呢?一般CPU有4-8个核心,而显卡则是100个左右的核心!不过由于造价还有空间的限制,显卡的某些功能会被削弱,例如逻辑判断功能(if) 。但是它的浮点运算能力却非常强大哦!
    为什么显卡要制作这么多核心呢?因为,在图像处理方面,是非常适合用并行计算的。所谓并行计算,就是两个并行计算之间不需要彼此的数据。而图像正是非常符合这个特点!想象一下,假如要在屏幕上显示一幅图画,那么可以交给这些核心这么一个工作:每个核心处理图画的每一行,把图画的每一行的像素画到屏幕上面。这种工作并行计算再适合不过了!因为这里面不包括任何的逻辑处理部分。 
    好了,是不是也想让显卡帮我们并行计算呢?

    **使用着色器功能会暂时关闭大量的OPENGL原有功能,包括光照等。你也可以在一次渲染中使用着色器和原有功能,在需要着色器的时候使用着色器,不需要的时候关闭就可以了

    要使用GLSL先要这样:
    #include <glew.h> 
    // 链接glew.lib (用工程属性或者预编译命令)
    // 创建OPENGL窗口 (用你最熟悉的创建方法)
    glewInit();
    GLuint program = glCreateProgram(); // 创建着色器程序
    GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
    // 你需要两个着色器,顶点着色器和片段着色器
    glShaderSource(vertex_shader, 1, &"这里是你的顶点着色器代码", &1);
    glShaderSource(fragment_shader, 1, &"这里是你的片段着色器代码", &1);
    // 别急着纠错,上面两行写的只是伪代码。正确的glShaderSource用法请自行搜索。在后面我会教你写基础的着色器代码。
    glCompileShader(vertex_shader);
    glCompileShader(fragment_shader);
    // 编译,可以用glGetShaderiv,指定GL_COMPILE_STATUS知道究竟有没有错误。如果你写的代码没有错,那么你的着色器就可以使用了,如果有错,可以用glGetShaderiv指定GL_INFO_LOG_LENGTH获得错误文本的长度,glGetShaderInfoLog获得错误文本
    glAttachShader(program, vertex_shader);
    glAttachShader(program, fragment_shader);
    // 关联你的着色器到程序上,删除关联用glDetachShader,这都是动态的
    glLinkProgram(program);
    // 链接你的程序,用所有着色器生成一个完整的着色器程序,可以用glGetProgramiv指定GL_LINK_STATUS知道究竟有没有错误。如果有错,可以用glGetProgramiv指定GL_INFO_LOG_LENGTH获取错误文本的长度,glGetProgramInfoLog获取错误文本
    glUseProgram(program);
    // 使用你的着色器程序,直到使用 glUseProgram(NULL)所有的OPENGL固定功能被关闭,使用你的着色器来处理

    好了,接下来我们来学习着色器语言,并且测试一下它
    如果你成功的话,你会看到这样的画面 
    图片

    这是一个光照的效果,根据公式光照能量跟距离光源的距离的平方成反比做出来的。你也可以不用着色器写,用你的CPU写也能达到一样的效果,但是着色器的速度更快(快得不止一点~)

    要实现这个效果我们需要知道光照的坐标、光照的颜色、光照的能量,因为是初学,这些都是定值就好了。假设光照在屏幕的正中央,颜色是黄色,能量是3000

    要达到这种效果,需要遍历屏幕上面的所有的像素,这就要用到一种叫做像素着色器的技术。
    像素着色器是这样一种着色器:顶点着色器只负责简单地传递坐标,主要的代码在片段着色器上面

    顶点着色器看起来是这样的:
    #version 440 // 关于version:这个是GLSL的version。我使用的是4.4版本,如果你的电脑不支持,请使用更低的版本,不过低版本有的语句需要修改,也有可能是你的高性能显卡没有打开,请到你的控制面板为你的程序选择高性能显卡,或者更改全局设置
     
    in vec4 vertex_position; // in表示由上层传递过来的数据,传递给顶点着色器的数据有坐标等,这里用到一个,就是坐标
     
    void main()
    {
    gl_Position = vertex_position; // 把接受到的坐标传递给片段着色器


    不要问我顶点着色器在一次渲染中执行了几次,因为我也不知道,在今后的学习中我会把这个问题搞懂的。我只知道片段着色器至少执行屏幕上像素个数的次数。

    片段着色器看起来是这样的:

    #version 440
     
    void main()
    {
    float r = 1.0f;
    float g = 1.0f;
    float b = 0.5f;
            // 黄色 
    vec2 p;
    p.x = gl_FragCoord.x - 500;
    p.y = gl_FragCoord.y - 500;
            // glFragCoord表示这个点的坐标
            // 在(500,500) 的地方生成光照。注意:因为OPENGL的计算方式,屏幕底部的Y坐标为0
    float length = p.x * p.x + p.y * p.y; // 距离的平方
    float rate = 3000.0f / length; // 比值
    gl_FragColor = vec4(rate * r, rate * g, rate * b, 1.0f); // glFragColor表示这个点的最终颜色


    **vec4等类型的用法可以自行搜索,都是挺简单的。

    接下来,你需要激活屏幕上面的所有像素,以便像素着色器的运行,用画一个矩形的方法再适合不过了
     
    glClear(GL_COLOR_BUFFER_BIT);
    glLoadIdentity();
    glBegin(GL_QUADS);
    glVertex2f(-1, -1);
    glVertex2f(-1, 1);
    glVertex2f(1, 1);
    glVertex2f(1, -1);
    glEnd();
    // SwapBuffers 或者 glFlush

    参考:《OpenGL编程指南》(原书第7版)
  • 相关阅读:
    Linux Virtual Server技术
    log4j+slf4j迁移到log4j2+slf4j (Servlet3.0)
    Android控件ToggleButton的使用方法
    Redis学习手冊(事务)
    游戏server之server优化思路
    Codeforces 474D Flowers (线性dp 找规律)
    【C语言】编写函数实现库函数atof
    unity3D游戏开发实战原创视频讲座系列7之消消乐游戏开发
    [WebGL入门]二十一,从平行光源发出的光
    TwoSum leetcode
  • 原文地址:https://www.cnblogs.com/tkgamegroup/p/4198811.html
Copyright © 2011-2022 走看看