heroboy
学徒
学徒
  • 粉丝5
  • 鲜花251朵
  • 威望4点
  • 社区居民
阅读:2745回复:12

[提问]关于自带BlurEffect实现的几个疑惑

楼主#
更多 发布于:2012-08-22 20:07
1.每次给GameObject添加BlurEffect.cs的时候,都自动把blurShader成员设置成“Hidden/BlurEffectConeTap”,怎么做到的?
虽然BlurEffect有ExecuteInEditMode属性,但代码中没有任何设置blurShader的地方啊。即使我把这个shader改个名字,还是自动设置了。
他们到底是怎么、在哪里关联在一起的啊?
2.在"Hidden/BlurEffectConeTap"这个shader的第一个subshader,是这样的:
SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant alpha}
SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}
SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}
SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}

 这4个SetTexture是用了4个不同的uv坐标吗?否则的话,我想这样加起来是没有意义的。
3.第二个subahder的cg里:
half4 _BlurOffsets;
这个uniform变量在哪里设置值的?这个应该不是buildin的变量吧。
4.解释一下BlitMultiTap,这是它的文档:
Description

Copies source texture into destination, for multi-tap shader.

This is mostly used for implementing some image effects. For example, Gaussian or iterative Cone blurring samples source texture at multiple different locations.

BlitMultiTap sets dest to be active render texture, sets source as _MainTex property on the material, and draws a full-screen quad. Each vertex of the quad has multiple texture coordinates set up, offset by offsets pixels.
尤其是最后一句什么意思,multiple texture coordinates是多少个?分别是怎么偏移的?
5.这个shader的参数:
Properties { _MainTex ("", any) = "" {} }
竟然是any,这是怎么回事?


喜欢0 评分0
heroboy
学徒
学徒
  • 粉丝5
  • 鲜花251朵
  • 威望4点
  • 社区居民
1楼#
发布于:2012-08-22 20:09
贴一下代码,供大家参考:
BlurEffect.cs
using UnityEngine;
using System.Collections;

[ExecuteInEditMode]
[AddComponentMenu("Image Effects/Blur")]
public class BlurEffect : MonoBehaviour
{    
    /// Blur iterations - larger number means more blur.
    public int iterations = 3;
    
    /// Blur spread for each iteration. Lower values
    /// give better looking blur, but require more iterations to
    /// get large blurs. Value is usually between 0.5 and 1.0.
    public float blurSpread = 0.6f;
    
    
    // --------------------------------------------------------
    // The blur iteration shader.
    // Basically it just takes 4 texture samples and averages them.
    // By applying it repeatedly and spreading out sample locations
    // we get a Gaussian blur approximation.
    
    public Shader blurShader = null;    

    //private static string blurMatString =

    static Material m_Material = null;
    protected Material material {
        get {
            if (m_Material == null) {
                m_Material = new Material(blurShader);
                m_Material.hideFlags = HideFlags.DontSave;
            }
            return m_Material;
        }
    }
    
    protected void OnDisable() {
        if( m_Material ) {
            DestroyImmediate( m_Material );
        }
    }    
    
    // --------------------------------------------------------
    
    protected void Start()
    {
        // Disable if we don't support image effects
        if (!SystemInfo.supportsImageEffects) {
            enabled = false;
            return;
        }
        // Disable if the shader can't run on the users graphics card
        if (!blurShader || !material.shader.isSupported) {
            enabled = false;
            return;
        }
    }
    
    // Performs one blur iteration.
    public void FourTapCone (RenderTexture source, RenderTexture dest, int iteration)
    {
        float off = 0.5f + iteration*blurSpread;
        Graphics.BlitMultiTap (source, dest, material,
            new Vector2(-off, -off),
            new Vector2(-off,  off),
            new Vector2( off,  off),
            new Vector2( off, -off)
        );
    }
    
    // Downsamples the texture to a quarter resolution.
    private void DownSample4x (RenderTexture source, RenderTexture dest)
    {
        float off = 1.0f;
        Graphics.BlitMultiTap (source, dest, material,
            new Vector2(-off, -off),
            new Vector2(-off,  off),
            new Vector2( off,  off),
            new Vector2( off, -off)
        );
    }
    
    // Called by the camera to apply the image effect
    void OnRenderImage (RenderTexture source, RenderTexture destination) {        
        RenderTexture buffer = RenderTexture.GetTemporary(source.width/4, source.height/4, 0);
        RenderTexture buffer2 = RenderTexture.GetTemporary(source.width/4, source.height/4, 0);
        
        // Copy source to the 4x4 smaller texture.
        DownSample4x (source, buffer);
        
        // Blur the small texture
        bool oddEven = true;
        for(int i = 0; i < iterations; i++)
        {
            if( oddEven )
                FourTapCone (buffer, buffer2, i);
            else
                FourTapCone (buffer2, buffer, i);
            oddEven = !oddEven;
        }
        if( oddEven )
            Graphics.Blit(buffer, destination);
        else
            Graphics.Blit(buffer2, destination);
        
        RenderTexture.ReleaseTemporary(buffer);
        RenderTexture.ReleaseTemporary(buffer2);
    }    
}
BlueEffectConTaps.shader
Shader "Hidden/BlurEffectConeTap" {
    Properties { _MainTex ("", any) = "" {} }
    SubShader {
        Pass {
            ZTest Always Cull Off ZWrite Off Fog { Mode Off }
            SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant alpha}
            SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}
            SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}
            SetTexture [_MainTex] {constantColor (0,0,0,0.25) combine texture * constant + previous}
        }
    }
    CGINCLUDE
    #include "UnityCG.cginc"
    struct v2f {
        float4 pos : POSITION;
        half2 uv : TEXCOORD0;
        half2 taps[4] : TEXCOORD1;
    };
    sampler2D _MainTex;
    half4 _MainTex_TexelSize;
    half4 _BlurOffsets;
    v2f vert( appdata_img v ) {
        v2f o;
        o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
        o.uv = v.texcoord - _BlurOffsets.xy * _MainTex_TexelSize.xy; // hack, see BlurEffect.cs for the reason for this. let's make a new blur effect soon
        o.taps[0] = o.uv + _MainTex_TexelSize * _BlurOffsets.xy;
        o.taps[1] = o.uv - _MainTex_TexelSize * _BlurOffsets.xy;
        o.taps[2] = o.uv + _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);
        o.taps[3] = o.uv - _MainTex_TexelSize * _BlurOffsets.xy * half2(1,-1);
        return o;
    }
    half4 frag(v2f i) : COLOR {
        half4 color = tex2D(_MainTex, i.taps[0]);
        color += tex2D(_MainTex, i.taps[1]);
        color += tex2D(_MainTex, i.taps[2]);
        color += tex2D(_MainTex, i.taps[3]);
        return color * 0.25;
    }
    ENDCG
    SubShader {
         Pass {
              ZTest Always Cull Off ZWrite Off
              Fog { Mode off }      

              CGPROGRAM
              #pragma fragmentoption ARB_precision_hint_fastest
              #pragma vertex vert
              #pragma fragment frag
              ENDCG
          }
    }
    Fallback off
}


回复(0) 喜欢(0)     评分
kuku小夭
论坛版主
论坛版主
  • 粉丝224
  • 鲜花1433朵
  • 威望178点
  • 社区居民
  • 原创先锋
  • 特殊贡献
2楼#
发布于:2012-08-22 22:02
  shader无力  ...
cocos2dx 转职成unity3d的苦手
回复(0) 喜欢(0)     评分
hog
hog
学者
学者
  • 粉丝309
  • 鲜花2930朵
  • 威望225点
  • 社区居民
  • 原创先锋
3楼#
发布于:2012-08-23 01:02
LZ的好学精神难能可贵
回答你的问题还真挺费神
 
1.这个问题我也有疑问。按理说要实现这样的自动化需要Editor脚本的支持,不过我并没有发现ImageEffects附带这样的脚本,也许是哪里隐含了这样的Editor脚本吧?以前问过类似的问题,被官方大神说用Shader.Find给忽悠了,显然这个Blur脚本本身没有用find。我试了一下,吧这个变量改成BlurShader1,系统就不会自动设置了,看来还是认变量名的反射。。。就是不知在哪干的。
 
2.这4条语句等于是把一个贴图像素用1/4亮度加了4次,看起来确实是没啥意义。大概是为了保持算法复杂度一致性还是为了代码统一?这第一个subshader是备用,在不支持blur的情况下才会用到。
 
3.这个_BlurOffsets确实是内置变量,你想想CS代码里有判断是否支持blur的判定,若不是内建的东西还判定个啥 。基本上可以这样理解:编译系统看见_BlurOffsets这个关键字,就会自动映射到某个固定的寄存器,并从中取数来进行Blur运算。这段shader代码
sampler2D _MainTex;
half4 _MainTex_TexelSize;
half4 _BlurOffsets;
v2f vert( appdata_img v ) {
    v2f o; 
    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    o.uv = v.texcoord - _BlurOffsets.xy * _MainTex_TexelSize.xy;
大致对应下面的编译后OpenGL代码:
Bind "vertex" Verte
Bind "texcoord" TexCoord0
Vector 5 [_MainTex_TexelSize]
Vector 6 [_BlurOffsets]
"!!ARBvp1.0
# 13 ALU
PARAM c[7] = { { 1, -1 },
        state.matrix.mvp,
        program.local[5..6] };
TEMP R0;
TEMP R1;
MOV R0.zw, c[6].xyxy;
MOV R0.xy, c[5];
MAD R0.xy, R0, -c[6], vertex.texcoord[0];

 这个_BlurOffsets被映射到了C6寄存器 然后进行运算。
这其实是刚接触GPU编程最难理解的,很多时候定义的“变量”只是个标识,表示应该去哪里取什么样的数进行操作,并不是一般编程语言里变量的概念.....
 
 
4. multiple texture coordinates是多少个?BlitMultiTap的定义写的很清楚了啊 :
static function BlitMultiTap (source : Texture, dest : RenderTexture, mat : Material, params offsets : Vector2[])  看见吗 offsets是个数组 前面有params,,就是长度不定啊,再看具体代码
    Graphics.BlitMultiTap (source, dest, material,
            new Vector2(-off, -off),
            new Vector2(-off,  off),
            new Vector2( off,  off),
            new Vector2( off, -off)
        );
调用的时候跟了4个Vector2,那就是4个偏移啦。
怎么偏移的?假设原点在屏幕左下角,- -就是左下 -+就是左上 ++就是右上,+-就是右下,很清楚的4点星形
其实研究过aa(反走样)的很熟悉这个了,可以这样理解:把该过程反过来,如果一个点星形的加权重画4次那就是4倍AA啦;4个星形的点加权的画在一起(平均)那正好是AA的逆过程,就成了走样(模糊)了,很好理解。
 
5.这个any表示不需要考虑它的类型,因为是被Camera的RenderImage设置的,就当它是贴图里的var自动变量就好了,any的意思是any texture。设置any还有一个隐含的好处,就是指定为any的贴图你是不能在Inspector里设定的,防止有人手贱出错.....
 
 
一般要完全弄懂shader相关的,需要从线性代数、计算机图形学、GPU原理、OPENGL、CG编程、Shader手册一路看下来,才能彻底搞明白。CG的东西基本上类似于并行化的嵌入式系统/固件编程,里面的语句大多是调用固定功能的组合,用一般流程化高级编程语言思想去研究,一开始是很费劲的,需要时间。
 
给点鲜花吧,这问题回答的老费劲了

 
 
 
回复(0) 喜欢(0)     评分
heroboy
学徒
学徒
  • 粉丝5
  • 鲜花251朵
  • 威望4点
  • 社区居民
4楼#
发布于:2012-08-23 09:47
2.这4条语句等于是把一个贴图像素用1/4亮度加了4次,看起来确实是没啥意义。大概是为了保持算法复杂度一致性还是为了代码统一?这第一个subshader是备用,在不支持blur的情况下才会用到。
我觉得第一个才是优先使用的吧。
When loading a shader, Unity will go through the list of subshaders, and pick the first one that is supported by the end user's machine.
3.这个_BlurOffsets确实是内置变量,你想想CS代码里有判断是否支持blur的判定,若不是内建的东西还判定个啥 。基本上可以这样理解:编译系统看见_BlurOffsets这个关键字,就会自动映射到某个固定的寄存器,并从中取数来进行Blur运算。
我看C6寄存器应该是个const寄存器吧,我的意思是没有看到在c#中有设置啊。文档中也没有说明unity自己回去设置这个寄存器啊。
最后,BlitMultiTap的offsets有几个是不是就分别以不同的offset画几次?例如:
BlitMultiTap (source, dest, material,
            new Vector2(-off, -off),
            new Vector2(-off,  off) );
就相当于:
BlitMultiTap (source, dest, material,new Vector2(-off, -off));
BlitMultiTap (source, dest, material,new Vector2(-off, off));
吗?
我觉得这也没有意义。因为shader输出像素的alpha是1,所以重复部分不会混合。
回复(0) 喜欢(0)     评分
heroboy
学徒
学徒
  • 粉丝5
  • 鲜花251朵
  • 威望4点
  • 社区居民
5楼#
发布于:2012-08-23 10:13
我做了一下试验:
void OnRenderImage (RenderTexture source ,RenderTexture destination)
    {
        RenderTexture.active = destination;
        GL.Clear(true,true,Color.black);
        Graphics.BlitMultiTap(source,destination,mat,offset1,offset2);
    }
这样子代码,offset1会使屏幕偏移offset1个像素,offset2对效果没影响。
然后,我给shader加上half4 _BlurOffsets;
v2f vert (appdata_base v)
            {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = v.texcoord.xy + _BlurOffsets.xy;
                return o;
            }  
结果,_BlurOffsets就是offset1的值。而如果_BlurOffsets只声明而不使用,则offset1随便怎么设置屏幕也不会偏移
 
 
 
 
回复(0) 喜欢(0)     评分
hog
hog
学者
学者
  • 粉丝309
  • 鲜花2930朵
  • 威望225点
  • 社区居民
  • 原创先锋
6楼#
发布于:2012-08-23 12:50
回 heroboy 的帖子
heroboy:我看C6寄存器应该是个const寄存器吧,我的意思是没有看到在c#中有设置啊。文档中也没有说明unity自己回去设置这个寄存器啊。
最后,BlitMultiTap的offsets有几个是不是就分别以不同的offset画几次?例 .. (2012-08-23 09:47)

1.系统在加载shader的时候会自动按照高级-低级的顺序加载,如果高级不支持会自动调用低级的,所以两个subshader的书写顺序是没有关系的,你大可以把两个subshader反过来书写。
 
2._BlurOffsets这个问题其实是两个步骤:C6寄存器的值是在BlitMultiTap这里设置的,至于如何设置的...你有兴趣可以反编译一下,无非是对应一些glUniform相关调用。我也测试了一下,这个值是:第一组Vertor2的值。正常情况下aa也好blur也好都是基于像素在不同方向等量偏移,所以取一组也够用了。Unity为啥要这么做,也许开发者有他们的理由。至于shader里的_BlurOffsets,是由Unity Shader编译器识别的一个Uniform,在OpenGL状态下对应C6寄存器,进行处理,当然C6的内容是已经由CPU代码加载好了。
 
3.重复绘制的意义,可以深入阅读一下图形学和OpenGL的基本资料,再仔细看看shader和cs脚本代码。alpha的作用只是混合时的一个权值而已,没有alpha当然也可以按照预设值混合的,比如0.5对0.5...,在这个shader里是把4次绘制各取0.25的权值相加进行的混合,shader脚本里写的很清楚。在GPU层面,只是一个传入-传出数值运算的过程,怎么算、由哪些参数来计算,完全是代码控制,所谓渲染无非是输入一组几何形状坐标、贴图数据、光照参数,运算后输出一组2D空间的色彩序列而已。只要愿意,不考虑有没有意义,把贴图a的alpha值加上贴图b的红色通道值进行混合,又有啥不可以呢....
 
事实上看shader代码你就能发现,虽然cs脚本里有4个vector2,但实际上shader只参考了第一组作为偏移量,后面的3个方向都是写死在shader里的,偏移量都是第一组的偏移量。我试了一下这样:Graphics.BlitMultiTap (source, dest, material,new Vector2(-off, -off)),结果是完全一样的。为啥要这样写死shader,可能是考虑兼容性和效率问题,gpu特别是老式gpu的寄存器的数量极为有限,而且用于blur运算也真不需要不等量的偏移。至于cs脚本里写了4组Vector2,可能一是为了代码便于理解,二是为了兼容日后新的shader吧。那个shader注释里不是意味深长的有一句么:hack, see BlurEffect.cs for the reason for this. let's make a new blur effect soon
 
Unity内置shader里各种奇怪很多,记得我早前的帖子就提到过。
回复(0) 喜欢(0)     评分
heroboy
学徒
学徒
  • 粉丝5
  • 鲜花251朵
  • 威望4点
  • 社区居民
7楼#
发布于:2012-08-23 13:15
在shader里面根本取不到Graphics.BlitMultiTap除第一个以外的其它offset啊
我试了 _BlurOffsets.zw
float4 _BlurOffsets1;
float4 _BlurOffsets[4];
各种方式。都只有第一个offset。
回复(0) 喜欢(0)     评分
hog
hog
学者
学者
  • 粉丝309
  • 鲜花2930朵
  • 威望225点
  • 社区居民
  • 原创先锋
8楼#
发布于:2012-08-23 13:21
回 heroboy 的帖子
heroboy:在shader里面根本取不到Graphics.BlitMultiTap除第一个以外的其它offset啊
我试了 _BlurOffsets.zw
float4 _BlurOffsets1;
float4 _BlurOffsets[4];
各种方式。都只有第一个offset。 (2012-08-23 13:15) 

上面回复了 确实只设置了第一组 外面的cs是伪代码
回复(0) 喜欢(0)     评分
hog
hog
学者
学者
  • 粉丝309
  • 鲜花2930朵
  • 威望225点
  • 社区居民
  • 原创先锋
9楼#
发布于:2012-08-23 21:56
关于脚本Public变量的默认值,这个问题俺低端了。
在project面板选中脚本,Inspector最上方会出现public量的设置,把需要的shader拖上去就是默认值了....
以前一直没注意过这个,对功能性的问题研究的确实比较少
回复(0) 喜欢(0)     评分
上一页
游客

返回顶部