cha*_*ian 8 random hlsl pixel-shader directx-9
我正在尝试编写一个非常简单的着色器,它可以为适用的对象添加随机闪烁.我想这样做的方法是在像素着色器中为像素值添加一个白色的随机阴影(R = G = B).
它似乎noise()不像我希望的那样工作:
float multiplier = noise(float3(Input.Position[0], Input.Position[1], time));
Run Code Online (Sandbox Code Playgroud)
它给我"错误X4532:无法将表达式映射到像素着色器指令集",指的是调用noise().
由于我不知道在着色器调用之间保留数字的方法,我认为我不能只根据渲染前传入的种子编写一个简单的随机数生成函数.
有没有办法从像素着色器内部生成一个随机数?如果有办法,怎么样?
Mic*_*007 23
更新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 );
}
Run Code Online (Sandbox Code Playgroud)
这是版本:
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);}
Run Code Online (Sandbox Code Playgroud)
传递纹理以产生噪声(通常)过度设计.有时它很方便,但对于大多数情况来说,计算随机数更简单,更快捷.
由于着色器变量对于每个片段是独立的,因此它们无法重用它们之间的现有变量.然后问题变成如何使用"好的"随机数种子.不合理的数字似乎适合开始的法案.然后,选择一个好的"置换"功能只是一个"简单"的问题.
这里有一些免费的代码可以解决这个问题:
// 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) ) ) );
}
Run Code Online (Sandbox Code Playgroud)
如果我们将公式分解为其组成部分,要了解其工作原理,可以更容易地看到正在发生的事情:
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.);
}
Run Code Online (Sandbox Code Playgroud)
将以上内容粘贴到:
我还创建了一个"比较"ShaderToy示例,其中包含2个噪声函数和2个随机函数:
使用噪音的演示"[2TC 15]斑点交叉淡化"
一个"经典"随机函数,有时称为snoise3这个坏函数:
return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);
Run Code Online (Sandbox Code Playgroud)
如果你想比较"伪随机"函数,请查看Dave的Hash而不使用正弦着色器.
当您想要像素着色器中的随机值时,通常要执行的操作是传入包含噪声的纹理.虽然它实际上不是"随机" - 但它看起来是随机的.
例如,这里有一些来自我躺着的像素着色器的代码:
float3 random = (tex2D(noiseTexture, texCoord * noiseScale + noiseOffset));
Run Code Online (Sandbox Code Playgroud)
我使用的纹理是RGB噪声纹理,有时会派上用场.但是相同的技术适用于灰度级技术.
通过缩放它,我确保噪声纹理中的像素排列到屏幕上的像素(您可能还希望将纹理采样器设置为"点"模式,这样您就不会模糊噪声纹理).
通过使用偏移量,您可以滚动纹理 - 这有点像播种随机数生成器.如果要避免"滚动"外观,请使用随机偏移.
没有什么可以说你必须重新使用种子来运行随机生成器,你只需要任何种子.
如果你使用像素坐标,那么你将得到一个确定性的结果(即像素x,y将始终具有相同的随机耀斑),但整体上它将随机分布.
现在,如果你可以输入一些根据环境而改变的东西(我对像素着色器一无所知),就像场景/相机组合的全局空间中像素的整体位置而不是相对于多边形然后,特别是在快速移动的环境中,你的结果会有效随机.
如果全局环境中的所有内容恰好相同,那么,是的,您将拥有完全相同的"随机"分布.但是这些因素中的任何一个都会发生变化(特别是基于用户输入,这可能是你最具动态性的"噪声源"),那么整体效果可能是"足够随机".
因此,关键的一点就是你的种子不必是以前运行随机数生成器的结果.它可以是任何东西.因此,根据着色器对您自己的RNG的输入为每个像素设计种子可能会为您提供所需的效果.