i_wanna_be_the_guy吧 关注:62,550贴子:1,119,308
  • 7回复贴,共1

【教程向】GM中的shader

取消只看楼主收藏回复

1L


1楼2016-07-18 12:12回复
    被人不信任了呢~ 还是发个以前写的教程证明自己吧
    窝知道大部分的人都用不到Gamemaker Studio (gms),但gms的IW引擎确实是有的,gms有个好处是可以发布到手机,而且还有物理引擎和shader等强大功能……
    如果你想实现某些IW里华丽的屏幕特效,如果你以后想做自己的独立游戏,或许可以看看~


    3楼2016-07-18 12:14
    回复
      额外漫画科普一下GPU并行运算的特点

      与CPU相比,GPU拥有更多逻辑运算单元(ALU)
      虽然单个的GPU运算单元会比CPU的运算单元慢,但GPU可以同时并行运算更多的像素
      但是GPU的控制流方面会弱于CPU,例如for循环,因此shader中要慎用if...else...、for之类的控制语句


      7楼2016-07-18 12:25
      回复
        变量的声明
        首先窝们来看看这个GMS默认的shader,一开始的几句就是变量的声明:
        attribute vec3 in_Position; // (x,y,z)
        //attribute vec3 in_Normal; // (x,y,z) unused in this shader.
        attribute vec4 in_Colour; // (r,g,b,a)
        attribute vec2 in_TextureCoord; // (u,v)
        varying vec2 v_vTexcoord;
        varying vec4 v_vColour;
        attribute、varying是OpenGL里的变量修饰符,还有个在这个shader里没有出现的uniform和const。 attribute、varying、uniform、const分别代表属性变量、易变变量、一致变量、常量,前三个简单解释如下:
        attribute: 变量只能在vertex shader里使用,通常用来表示顶点的数据
        varying: 变量用于vertex和fragment shader之间的数据传递,vertex shader的输出,进行插值输入到fragment shader
        uniform: 由外部(gml)传递给shader的变量,通常用于纹理(texture),对于vertex和fragment shader变量是只读的
        接下来是数据类型
        vec2、vec3、vec4分别代表二维向量、三维向量、四维向量,呃你可以理解为包含了多少个元素的数组吧~例如vec4的in_Colour就包含了rgba四个元素
        要使用in_Colour的第二个元素(这里是g,绿色),可以用in_Colour.g,还可以使用in_Colour[1],rgba这些属性名只是让程序可读性更强,并不代表真的颜色值,你也可以在颜色无关的向量使用,例如in_Position.r,但为了可读性还是用约定俗成的规矩吧。还有代表空间坐标的xyz和纹理坐标的uv。
        要初始化可以使用构造函数:
        vec2 a = vec2(1.0, 2.0);
        vec4 b = vec4(0.0, 0.0, 0.0, 1.0);
        GLSL的基本类型有float,int,bool
        float a,b;
        int c = 2;
        bool d = true;
        和c语言差不多,但注意shader里不能进行显式或隐式的类型转换,例如:
        float c = 1 + 2.0; //隐式类型转换,编译报错
        int d = (int)0.5 + 1; //显示类型转换,编译报错
        要使用构造函数:
        float c = float(1) + 2.0;
        int d = int(0.5) + 1;
        GLSL还包括2×2、3×3或4×4型矩阵类型: mat2, mat3, mat4
        读取texture的采样器(sampler): sampler1D, sampler2D,sampler3D
        因为在目前的默认shader没出现,所以以后再说啦~


        8楼2016-07-18 12:25
        回复
          _(:3」∠)_ 感动,发图贴度娘终于不删了


          9楼2016-07-18 12:26
          回复
            前面讲的是全局变量的声明
            现在来看看void main(),学过C语言都知道main的吧~ 在GLSL里也一样,无论vertex还是fragment shader都必须执行main函数,所有顶点或像素的主要操作都在里面了。
            main函数里的前两句:
            vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
            gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
            把in_Position的xyz赋值到自定义四维向量object_space_pos中,用1.0补充第四个分量。坐标的第四个分量,如果是1代表位置,是0代表方向,所以这里用1.0。in_Position是GMS固定用来识别顶点坐标位置的变量,在之前的attribute vec3 in_Position声明,在2D中其xy分量分别代表sprite矩形的四个顶点坐标,z暂时不必管。in_Position是顶点的物体空间(object space)坐标,这里由vec3转换成vec4,是为了接下来进行的坐标空间变换左乘一个4x4的变换矩阵。由于2D很少涉及复杂的空间变换,这里只简单解释一下。
            不同的坐标空间有不同的坐标系,于是描述物体的位置坐标就发生了变化。比如,你发现隔壁老王在你正前方10米,面向左方干些奇怪的事情,在你的坐标空间x轴为右y轴为前的话,描述老王的位置可以用(0,10)坐标来表示,那么在老王的坐标空间,你在老王正左边,而老王又习惯用厘米,那么老王描述你的位置可以用(-1000,0)坐标来表示。空间变换就是转换坐标系这么一个过程,数学上通常是 4x4变换矩阵*四维位置坐标。谁乘以谁的顺序很重要,写反了会得出完全不一样的结果哦!矩阵gms已经帮你准备好了,就是gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION]。
            in_Position的object space,其实就是gml里room的坐标。vec4化后左乘那个矩阵,最后映射到投影空间(projection space)保存到gl_Position。对于投影空间,只考虑xy,就是以屏幕中心为原点建立的二维坐标系,坐标映射到[-1,1]。也就是,屏幕左下角为(-1,-1),中心点为(0,0),右上角为(1,1)。z通常代表深度,类似gml的depth,在这个shader里恒为0.5。这么空间变换的目的是硬件要这么做才能正确绘制。


            12楼2016-07-18 13:18
            回复
              main函数的最后两句:
              v_vColour = in_Colour;
              v_vTexcoord = in_TextureCoord;
              in_Colour是顶点颜色,in_TextureCoord是顶点坐标,GMS固定的顶点属性变量。v_vColour、v_vTexcoord是自定义的varying变量,前面说过,vertex shader是将这些值经过渲染管线插值然后传递到fragment shader的,fragment shader才因此可以使用。随便用顶点颜色举个栗子大致过程是:

              Fragment Shader的中间像素值是根据Vertex shader的值线性插入的。其他varying变量也是这个原理。


              13楼2016-07-18 13:19
              收起回复
                好了,vertex shader已经在上面大致讲完了,接下来就是fragment shader~
                varying vec2 v_vTexcoor;
                varying vec4 v_vColour;
                一开始就是熟悉的v_vColour、v_vTexcoord变量声明,代表它们已经从顶点插值传过来了~!fragment shader很简单,main函数就这么一句:
                void main()
                {
                gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoor );
                }
                texture2D是纹理映射函数,就是根据纹理坐标作为参照点,绘制纹理中的哪一部分。函数的两个参数分别是 需要绘制的纹理 和 纹理坐标,返回vec4就是对应的颜色值。一张Texture的纹理坐标,y轴是向下的,左上角是(0, 0),右下角是(1.0, 1.0)。具体用这图感受一下:

                但要注意,这里的texture是gm_BaseTexture,gms会把所有的sprite和background都打包进一张或多张大的texture里做成图集(atlas)来提高绘制效率。当你导入官方的shader范例并编译,gm_BaseTexture实际会是这样的:

                也就是一个object的sprite之间的顶点可能是(0.976, 0.012) (0.979, 0.015)这样差值小且很奇怪的数,不过vertex shader的in_TextureCoord已经帮你处理好了,插值后的v_vTexcoor能正确显示。texture2D返回的颜色值乘以插值后的顶点颜色v_vColour,是texture颜色值偏向该顶点颜色绘制,最后赋值给gl_FragColor决定该像素最后的颜色。
                至此,gms的整个默认shader已经大致讲完了,主要偏向原理的= =
                如果有更新或许会讲官方的范例


                14楼2016-07-18 13:19
                回复