2011-03-01 81 views
9

我正在尝试编写一个非常简单的着色器,它将随机闪烁添加到适用的对象中。我希望这样做的方法是在像素着色器中为像素值添加随机阴影(R = G = B)。我可以在像素着色器中生成一个随机数吗?

看来,noise()不工作,我希望它的方式:

float multiplier = noise(float3(Input.Position[0], Input.Position[1], time)); 

它给了我“错误X4532:不能映射表达像素着色指令集”指的是呼叫noise()

由于我不知道如何在调用着色器之间保留一个数字,我不认为我可以根据渲染前传入的种子编写一个简单的随机数生成函数。

有没有办法在像素着色器中产生一个随机数?如果有办法,怎么样?

回答

5

没有什么说你必须重复使用随机发生器的种子从运行到运行,你只需要任何种子。如果你使用像素坐标,那么你将得到一个确定性的结果(即像素x,y总是会有相同的随机光晕),但整体而言,整个人脸将随机分布。

现在,如果您有可用的输入信息,这些信息会根据环境而改变(我对像素着色器没有任何了解),比如场景/相机组合中全局空间像素的整体位置相对于多边形本身,那么,尤其是在快速移动的环境中,您的结果将会有效地随机。

如果全球环境中的一切恰好相同,那么,是的,你将拥有完全相同的“随机”分布。但是这些因素中的任何一个都会改变(特别是基于用户输入,它可能是你最有活力的“噪声源”),那么整体效果可能会“足够随机”。

所以,关键是要避免你的种子不必是以前运行随机数生成器的结果。它可以是任何东西。因此,根据着色器对您自己的RNG的输入为每个像素设计一个种子,可能会为您提供所需的效果。

+0

虽然这两个答案都有帮助,但这个更适合我所寻找的。谢谢! – chaosTechnician 2011-03-06 03:22:31

8

当你想要一个像素着色器中的随机值时,你通常会做的是传递一个包含噪声的纹理。虽然它实际上不是“随机” - 它看起来随机。

例如,这里是从像素着色器的一些代码,我已经躺在附近:

float3 random = (tex2D(noiseTexture, texCoord * noiseScale + noiseOffset)); 

我使用的纹理是RGB噪声纹理,它可以派上用场了一些时间。但是同样的技术可以用于灰度级。

通过缩放它,我确保了噪点纹理中的像素与屏幕像素对齐(您可能还希望将纹理采样器设置为“点”模式,以免模糊噪点纹理)。

通过使用偏移量,您可以滚动纹理 - 这就像播种随机数发生器。如果要避免“滚动”外观,请使用随机偏移量。

24

更新2017年7月:我做了 “伪随机性” 更稳定

// Version 3 
float random(vec2 p) 
{ 
    vec2 K1 = vec2(
     23.14069263277926, // e^pi (Gelfond's constant) 
     2.665144142690225 // 2^sqrt(2) (Gelfond–Schneider constant) 
    ); 
    return fract(cos(dot(p,K1)) * 12345.6789); 
} 

这是版本:

float random(vec2 p) 
{ 
    // e^pi (Gelfond's constant) 
    // 2^sqrt(2) (Gelfond–Schneider constant) 
    vec2 K1 = vec2(23.14069263277926, 2.665144142690225); 

    //return fract(cos(mod(12345678., 256. * dot(p,K1)))); // ver1 
    //return fract(cos(dot(p,K1)) * 123456.); // ver2 
    return fract(cos(dot(p,K1)) * 12345.6789); // ver3 
} 

// Minified version 3: 
float random(vec2 p){return fract(cos(dot(p,vec2(23.14069263277926,2.665144142690225)))*12345.6789);} 

在纹理产生噪声传递(通常)超过工程设计。有些时候它很方便,但是对于大多数情况,只需计算一个随机数就简单快捷。

由于着色器变量是独立于每个片段的,因此它们无法在它们之间重新使用现有变量。问题就成为如何使用“好”随机数种子的问题之一。不合理的数字似乎符合开始的法案。那么选择一个好的“排列”功能只是一个'简单'的问题。

下面是一些免费代码做的伎俩:

// Input: It uses texture coords as the random number seed. 
// Output: Random number: [0,1), that is between 0.0 and 0.999999... inclusive. 
// Author: Michael Pohoreski 
// Copyright: Copyleft 2012 :-) 
// NOTE: This has been upgraded to version 3 !! 
float random(vec2 p) 
{ 
    // We need irrationals for pseudo randomness. 
    // Most (all?) known transcendental numbers will (generally) work. 
    const vec2 r = vec2(
    23.1406926327792690, // e^pi (Gelfond's constant) 
    2.6651441426902251); // 2^sqrt(2) (Gelfond–Schneider constant) 
    return fract(cos(mod(123456789., 1e-7 + 256. * dot(p,r)))); 
} 

要理解它是如何工作的,如果我们打破公式分解成它的组成部分变得更容易想象这是怎么回事:

const vec2 k = vec2(23.1406926327792690,2.6651441426902251); 
float rnd0(vec2 uv) {return dot(uv,k); } 
float rnd1(vec2 uv) { return 1e-7 + 256. + dot(uv,k); } 
float rnd2(vec2 uv) { return mod(123456789., 256. * dot(uv,k)); } 
float rnd3(vec2 uv) { return cos(mod(123456789., 256. * dot(uv,k))); } 

// We can even tweak the formula 
float rnd4(vec2 uv) { return fract(cos(mod(1234., 1024. * dot(uv,k)))); } 
float rnd5(vec2 uv) { return fract(cos(mod(12345., 1024. * dot(uv,k)))); } 
float rnd6(vec2 uv) { return fract(cos(mod(123456., 1024. * dot(uv,k)))); } 
float rnd7(vec2 uv) { return fract(cos(mod(1234567., 1024. * dot(uv,k)))); } 
float rnd8(vec2 uv) { return fract(cos(mod(12345678., 1024. * dot(uv,k)))); } 
float rnd9(vec2 uv) { return fract(cos(mod(123456780., 1024. * dot(uv,k)))); } 

void mainImage(out vec4 fragColor, in vec2 fragCoord) 
{ 
    mediump vec2 uv = fragCoord.xy/iResolution.xy; 
    float i = rnd9(uv); 
    fragColor = vec4(i,i,i,1.); 
} 

粘贴到上述:

我还创建与2层噪声的功能,和图2个随机函数 “比较” ShaderToy例如:

演示“[2TC 15]散斑交叉淡出”

“经典”随机函数,有时也被称为snoise3是这样bad one

return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); 

如果你想比较“伪随机”功能检查Dave的Hash without sine着色器。

+0

好的:D谢谢! – Raptormeat 2012-09-16 08:36:12

+0

有趣。虽然我不同意纹理方法是过度工程。您正在交易纹理提取以获取不重要的指令。而且你仍然必须通过每帧的随机偏移。我会说这两种方法都是有效的。 – 2012-10-10 12:07:58

+0

@AndrewRussell随着现代GPU的(通常)做计算(CPU绑定)然后纹理查找(IO绑定)是(通常)快得多。如果你需要每个像素有一个“随机”数字,只需传入纹理坐标就很简单。我通常把这个词放在括号里,因为* * * *平台的唯一方法就是同时处理这两个问题。 ;-) – Michaelangel007 2012-10-11 19:36:25

相关问题