Random 我可以在像素着色器中生成随机数吗?

Random 我可以在像素着色器中生成随机数吗?,random,hlsl,directx-9,pixel-shader,Random,Hlsl,Directx 9,Pixel Shader,我正在尝试编写一个非常简单的着色器,为适用对象添加随机闪烁。我想这样做的方法是在像素着色器内的像素值中添加一个随机的白色阴影(R=G=B) 似乎noise()的工作方式与我希望的不同: float multiplier = noise(float3(Input.Position[0], Input.Position[1], time)); 它给出了“错误X4532:无法将表达式映射到像素着色器指令集”,引用了对noise()的调用 因为我不知道如何在调用着色器之间保留一个数字,所以我不认为我可

我正在尝试编写一个非常简单的着色器,为适用对象添加随机闪烁。我想这样做的方法是在像素着色器内的像素值中添加一个随机的白色阴影(R=G=B)

似乎
noise()
的工作方式与我希望的不同:

float multiplier = noise(float3(Input.Position[0], Input.Position[1], time));
它给出了“错误X4532:无法将表达式映射到像素着色器指令集”,引用了对
noise()
的调用

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


有没有办法从像素着色器内部生成随机数?如果有办法,怎么做?

没有什么规定你必须在一个随机生成器运行到另一个运行时重复使用种子,你只需要任何种子

如果使用(比如)像素坐标,则最终将得到确定性结果(即像素x、y始终具有相同的随机光斑),但总体而言,整个人脸将随机分布

现在,如果您可以输入一些根据环境变化的内容(我对像素着色器一无所知),比如场景/摄影机组合的全局空间中像素的总体位置,而不是相对于多边形本身,那么,特别是在快速移动的环境中,您的结果实际上是随机的

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


因此,关键在于,您的种子不必是先前运行的随机数生成器的结果。它可以是任何东西。因此,基于着色器到您自己的RNG的输入为每个像素设计种子可能会给您所需的效果。

当您想要像素着色器中的随机值时,通常要做的是传入包含噪波的纹理。虽然它实际上不是“随机的”——但它看起来是随机的

例如,这里有一些来自我周围的像素着色器的代码:

float3 random = (tex2D(noiseTexture, texCoord * noiseScale + noiseOffset));
我使用的纹理是RGB噪波纹理,它有时会派上用场。但同样的技术也适用于灰度图像

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


通过使用偏移,你可以滚动纹理-这有点像播种一个随机数生成器。如果要避免“滚动”的外观,请使用随机偏移量。

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.);
}
将上述内容粘贴到:

我还创建了一个“比较”ShaderToy示例,其中包含2个噪波函数和2个随机函数:

使用噪声“[2TC 15]散斑交叉衰减”的演示

“经典”随机函数,有时称为
snoise3
,如下所示:


如果要比较“伪随机”函数,请查看Dave的着色器。

如果可以获得整个帧的校验和并将其用作种子,该怎么办。
这样,如果帧上的任何内容正在移动,则尝试随机化的选择将随帧移动

虽然这两个答案都很有用,但这一个更符合我的要求。谢谢有趣。虽然我不同意纹理方法过于工程化。您正在用一个纹理提取来换取数量不小的指令。你仍然需要在每一帧中传递一个随机偏移量。我认为这两种方法都是有效的。@AndrewRussell使用现代GPU进行计算(通常)要比纹理查找(IO)快得多。如果需要每个像素的“随机”数,只需传入纹理坐标即可。我通常把这个词放在括号里,因为要知道你的平台,唯一的方法就是同时计时@Michaelangelo这到底是怎么回事?此外,更多的视觉模式开始以更高的分辨率出现。最好将其分解,看看每一步是如何慢慢移动到伪随机输出的。i、 复制粘贴下一篇帖子到[link]www.shadertoy.com/new[/link]@WeiXiang这个把戏在我之前很久就在“demoscene”中被知道了——据我所知,我不相信有人记录过它。要点是,随机数生成器只是一个输出很难猜测的函数。大多数(全部?)线性同余生成器是确定性的。选择Cos()是因为Cos(0)==1,但您可以使用除零之外的任何“相位偏移”,比如使用您最喜欢的无理数。
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);