i_wanna_be_the_guy吧 关注:62,552贴子:1,119,307

【教程向】GM中的shader

只看楼主收藏回复

1L


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


    3楼2016-07-18 12:14
    回复


      IP属地:江苏来自Android客户端5楼2016-07-18 12:21
      收起回复
        额外漫画科普一下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
            回复
              字词质子结界


              IP属地:广东来自Android客户端10楼2016-07-18 12:31
              收起回复
                不是很懂你们大神


                IP属地:江西11楼2016-07-18 13:16
                收起回复
                  前面讲的是全局变量的声明
                  现在来看看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
                      回复
                        高端,帮顶


                        来自Android客户端16楼2016-07-18 13:31
                        收起回复
                          我不是很懂,我只是指出在init文件夹下新增房间可能引起的bug,并没有和你抬杠,也没有怀疑你的能力


                          IP属地:广东来自Android客户端17楼2016-07-18 13:31
                          收起回复
                            gms太贵了


                            来自Android客户端19楼2016-07-18 15:47
                            收起回复