I'm using Go and compiling it to web assembly.
I'm trying to render a bunch of rectangles next to eachother with a random colour, but they keep rendering as just gray.
My render function looks something like this:
for row,_ := range rows {
for col,_ := range row {
ctx.Set("fillStyle", fmt.Sprintf("#%06x", rand.Int()))
ctx.Call("fillRect", 20, 20 + (col * width), maxHeight - (row*height))
}
}
With which it renders a big block (all rectangles are next to eachother) but just all in gray, instead of doing them in different colours.
Is this enough code in the example to help further? If not I can post it to a gist, as I'm new to WASM I'm unsure which parts could really be relevant - but those 2 functions are the only ones doing something with rendering as far as I can tell.
The problem is that you use this expression to construct the fill style:
fmt.Sprintf("#%06x", rand.Int())
rand.Int() returns a non-negative pseudo-random int. Size of int is 64 bits if GOOS=js and GOARCH=wasm. What this means is that the random int number will be random 8 bytes (first bit being always 0 due to being non-negative).
If you format such a number with the %06x verb, like almost all the time it will be more than just 6 hex digits. The width 6 means to be at least 6, and the flag 0 means to pad with zeros if less. But if it's longer, it is not truncated.
And if you set an invalid color to canvas.fillStyle, it will disregard it and the last set valid fill style will remain active. And I'm guessing it was a gray color you used before the loop.
Fix is easy, just make sure the random number has no more than 3 bytes, or in other words, 6 hex digits. Use a simple bitmask:
ctx.Set("fillStyle", fmt.Sprintf("#%06x", rand.Int()&0xffffff))
Or use rand.Intn() instead of rand.Int():
ctx.Set("fillStyle", fmt.Sprintf("#%06x", rand.Int(0x1000000)))
Also context.fillRect() expects 4 arguments: x, y, width and height, so it should be something like this:
ctx.Call("fillRect", 20+(col*width), maxHeight-(row*height), width, height)
Related
I knew that a range of float color value in a shader [0..1] is mapped into range of [0..255] in UCHAR buffer.
According to this, I was expecting for steps of size of 1/255 in shader color values for each change in UCHAR buffer.
But the results were surprisingly different. Here is for the first two steps:
Red float value in Shader -> UCHAR value in a read Buffer
0.000000 -> 0
0.002197 -> 0
0.002198 -> 1
0.006102 -> 1
0.006105 -> 2
The first two steps are around 0.002197 and 0.006102 which are different than the expected steps: 0.00392 and 0.00784.
So what is the mapping formula ?
Unsigned integer normalization is based on the formula f = i/INT_MAX, where f is the floating point value (after clamping to [0, 1]), i is the integer value, and INT_MAX is the maximum integer value for the integer's bitdepth (255) in this case.
So if you have a float, and want the unsigned, normalized integer value of it, you use i = f * INT_MAX. Of course... integers do not have the same precision as floats. So if the result of f * INT_MAX is 0.5, what is the integer value of that? It could be 0, or it could be 1, depending on how things are rounded.
Implementations are permitted to round integer values in any way they prefer. They are encouraged to use nearest rounding (the post-conversion 0.49 would become 0, and 0.5 would become 1), but that is not a requirement. The only requirements are that it must pick one of the two nearest values (it can't turn 0.5 into 3) and that the exact floating-point values of 0.0 and 1.0 (which includes any values clamped to them) must be exactly represented as integer 0 and INT_MAX.
If you have an explicit need to have direct rounding, you can always do the normalization yourself. In fact, GLSL has specific functions to help you. The following assumes that you are trying to write to a texture with the Vulkan format R8G8B8A8_UNORM, and we're assuming you're writing to a storage image, not via outputs from the fragment shader (you can do that too, but you lose blending).
So, step 1 is to change your layout format to be r32ui. That is, you are now writing an unsigned 32-bit value, rather than 4 unsigned 8-bit normalized values. That's perfectly valid.
Step 2 is to employ the packUNorm4x8 function. This function does float-to-integer normalization, but the specification explicitly performs rounding correctly. Use the return value of that function in your imageStore function, and you're fine.
If you want to write to a fragment shader output, that's a bit more complex. There, you will need to use a different image view, one that uses the R32_UINT format. So you're creating a 32-bit unsigned integer view of a 4x8-bit normalized texture. That has to become a render target, so you're going to have to do subpass surgery. From there, just write the result of packUNorm4x8.
Of course, you immediately lose blending and similar operations, since you're writing integers values. And since you had to do that subpass surgery, it's likely that any shader writing to it will need to do this too.
Also, note that in both cases, you will likely need to adjust the order of the components of the value you write. packUNorm4x8 is explicitly defined to be little endian, whereas (I believe?) R8G8B8A8 is specified to be in that order, most-significant to least. So you'll probably need to essentially do endian swapping with packUNorm4x8(value.abgr).
I am trying get 3 new random floats into my pixel shader for each pixel. Based on what I have read here, here, and also here, I believe that I need to generate a large texture containing random RGB values and then during each draw call randomly generate a couple of texture coordinate offset values to produce a pseudo-random effect. Is the only way to do this through the LockRect and UnlockRect API? I hope not.
They only way I have found to do this is the lock and unlock rectangle method. But it is much easier then I initially thought. Here is how I filled the texture.
'Create Random texture for dithering shader
rando = New Random()
randomText = New Texture(device, 1000, 1000, 1, Usage.Dynamic,
Format.A16B16G16R16, Pool.Default)
'89599
'89510
Dim data(1000 * 1000 * 8 + 1000 * 63 + 936) As Byte
rando.NextBytes(data)
Dim dataBox As DataRectangle =
randomText.GetSurfaceLevel(0).LockRectangle(LockFlags.None)
dataBox.Data.Seek(0, IO.SeekOrigin.Begin)
dataBox.Data.Write(data, 0, data.Length)
dataBox.Data.Close()
As you can see from the code I had to add a lot of extra bytes to completely fill the texture with random values. I used a 1000 x 1000 64bit texture so you would think I would need 1000*1000*8 Bytes of data but I need an extra 63936 Bytes to fill the texture and I am not sure why. But it seems to work for my needs.
I have a hunch this has been done before but I am a total layman at this and don't know how to begin to ask the right question. So I will describe what I am trying to do...
I have an unknown ARGB color. I only know its absolute RGB value as displayed over two known opaque background colors, for example black 0x000000 and white 0xFFFFFF. So, to continue the example, if I know that the ARGB color is RGB 0x000080 equivalent when displayed over 0x000000 and I know that the same ARGB color is RGB 0x7F7FFF equivalent when displayed over 0xFFFFFF, is there a way to compute what the original ARGB color is?
Or is this even possible???
So, you know that putting (a,r,g,b) over (r1,g1,b1) gives you (R1,G1,B1) and that putting it over (r2,g2,b2) gives you (R2,G2,B2). In other words -- incidentally I'm going to work here in units where a ranges from 0 to 1 -- you know (1-a)r1+ar=R1, (1-a)r2+ar=R2, etc. Take those two and subtract: you get (1-a)(r1-r2)=R1-R2 and hence a=1-(R1-R2)/(r1-r2). Once you know a, you can work everything else out.
You should actually compute the values of a you get from doing that calculation on all three of {R,G,B} and average them or something, to reduce the effects of roundoff error. In fact I'd recommend that you take a = 1 - [(R1-R2)sign(r1-r2) + (G1-G2)sign(g1-g2) + (B1-B2)sign(b1-b2)] / (|r1-r2|+|g1-g2|+|b1-b2), which amounts to weighting the more reliable colours more highly.
Now you have, e.g., r = (R1-(1-a)r1)/a = (R2-(1-a)r2)/a. These two would be equal if you had infinite-precision values for a,r,g,b, but of course in practice they may differ slightly. Average them: r = [(R1+R2)-(1-a)(r1+r2)]/2a.
If your value of a happens to be very small then you'll get only rather unreliable information about r,g,b. (In the limit where a=0 you'll get no information at all, and there's obviously nothing you can do about that.) It's possible that you may get numbers outside the range 0..255, in which case I don't think you can do better than just clipping.
Here's how it works out for your particular example. (r1,g1,b1)=(0,0,0); (r2,g2,b2)=(255,255,255); (R1,G1,B1)=(0,0,128); (R2,G2,B2)=(127,127,255). So a = 1 - [127+127+127]/[255+255+255] = 128/255, which happens to be one of the 256 actually-possible values of a. (If it weren't, we should probably round it at this stage.)
Now r = (127-255*127/255)*255/256 = 0; likewise g = 0; and b = (383-255*127/255)*255/256 = 255.
So our ARGB colour was 80,00,00,FF.
Choosing black and white as the background colors is the best choice, both for ease of calculation and accuracy of result. With lots of abuse of notation....
a(RGB) + (1-a)0xFFFFFF = 0x7F7FFF
a(RGB) + (1-a)0x000000 = 0x000080
Subtracting the second from the first...
(1-a)0xFFFFFF = 0x7F7FFF-0x000080 = 0x7F7F7F
So
(1-a) = 0x7F/0xFF
a = (0xFF-0x7F)/0xFF = 0x80/0xFF
A = 0x80
and RGB = (a(RGB))/a = 0x000080/a = 0x0000FF
You can do something very similar with other choices of background color. The smaller a is and the closer the two background colors are the less accurately you will be able to determine the RGBA value. Consider the extreme cases where A=0 or where the two background colors are the same.
I'm trying to control some RGB LEDs and fade from red to violet. I'm using an HSV to RGB conversion so that I can just sweep from hue 0 to hue 300 (beyond that it moves back towards red). The problem I noticed though is that it seems to spend far to much time in the cyan and blue section of the spectrum. So I looked up what the HSV spectrum is supposed to look like, and found thisL
I didn't realize that more than half the spectrum was spent between green and blue.
But I'd really like it to look much more like this:
With a nice even blend of that "standard" rainbow colors.
I'd imagine that this would end up being some sort of s-curve of the normal hue values, but am not really sure how to calculate that curve.
An actual HSV to RGB algorithm that handles this internally would be great (any code really, though it's for an Arduino) but even just an explanation of how I could calculate that hue curve would be greatly appreciated.
http://www.fourmilab.ch/documents/specrend/ has a fairly detailed description of how to convert a wavelength to CIE components (which roughly correspond to the outputs of the three kinds of cone sensors in your eyes) and then how to convert those to RGB values (with a warning that some wavelengths don't have RGB equivalents in a typical RGB gamut).
Or: there are various "perceptually uniform colour spaces" like CIE L*a*b* (see e.g. http://en.wikipedia.org/wiki/Lab_color_space); you could pick one of those, take equal steps along a straight line joining your starting and ending colours in that space, and convert to RGB.
Either of those is likely to be overkill for your application, though, and there's no obvious reason why they should be much -- or any -- better than something simpler and purely empirical. So why not do the following:
Choose your starting and ending colours. For simplicity, let's suppose they have S=1 and V=1 in HSV space. Note them down.
Look along the hue "spectrum" that you posted and find a colour that looks to you about halfway between your starting and ending points. Note this down.
Now bisect again: find colours halfway between start and mid, and halfway between mid and end.
Repeat once or twice more, so that you've divided the hue scale into 8 or 16 "perceptually equal" parts.
Convert to RGB, stick them in a lookup table, and interpolate linearly in between.
Tweak the RGB values a bit until you have something that looks good.
This is totally ad hoc and has nothing principled about it at all, but it'll probably work pretty well and the final code will be basically trivial:
void compute_rgb(int * rp, int * gp, int * bp, int t) {
// t in the range 0..255 (for convenience)
int segment = t>>5; // 0..7
int delta = t&31;
int a=rgb_table[segment].r, b=rgb_table[segment+1].r;
*rp = a + ((delta*(b-a))>>5);
a=rgb_table[segment].g; b=rgb_table[segment+1].g;
*gp = a + ((delta*(b-a))>>5);
a=rgb_table[segment].b; b=rgb_table[segment+1].b;
*bp = a + ((delta*(b-a))>>5);
}
(you can make the code somewhat clearer if you don't care about saving every available cycle).
For what it's worth, my eyes put division points at hue values of about (0), 40, 60, 90, 150, 180, 240, 270, (300). Your mileage may vary.
FastLED does a a version of this: https://github.com/FastLED/FastLED/wiki/FastLED-HSV-Colors
HSLUV is another option: http://www.hsluv.org/. They have libraries in a bunch of different languages.
Also, this is an interesting technique: https://www.shadertoy.com/view/4l2cDm
const float tau = acos(-1.)*2.;
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
vec3 rainbow = sqrt( //gamma
sin( (uv.x+vec3(0,2,1)/3.)*tau ) * .5 + .5
);
fragColor.rgb = rainbow;
}
Also see:
https://en.wikipedia.org/wiki/Rainbow#Number_of_colours_in_spectrum_or_rainbow for more info.
I have code that needs to render regions of my object differently depending on their location. I am trying to use a colour map to define these regions.
The problem is when I sample from my colour map, I get collisions. Ie, two regions with different colours in the colourmap get the same value returned from the sampler.
I've tried various formats of my colour map. I set the colours for each region to be "5" apart in each case;
Indexed colour
RGB, RGBA: region 1 will have RGB 5%,5%,5%. region 2 will have RGB 10%,10%,10% and so on.
HSV Greyscale: region 1 will have HSV 0,0,5%. region 2 will have HSV 0,0,10% and so on.
(Values selected in The Gimp)
The tex2D sampler returns a value [0..1].
[ I then intend to derive an int array index from region. Code to do with that is unrelated, so has been removed from the question ]
float region = tex2D(gColourmapSampler,In.UV).x;
Sampling the "5%" colour gave a "region" of 0.05098 in hlsl.
From this I assume the 5% represents 5/100*255, or 12.75, which is rounded to 13 when stored in the texture. (Reasoning: 0.05098 * 255 ~= 13)
By this logic, the 50% should be stored as 127.5.
Sampled, I get 0.50196 which implies it was stored as 128.
the 70% should be stored as 178.5.
Sampled, I get 0.698039, which implies it was stored as 178.
What rounding is going on here?
(127.5 becomes 128, 178.5 becomes 178 ?!)
Edit: OK,
http://en.wikipedia.org/wiki/Bankers_rounding#Round_half_to_even
Apparently this is "banker's rounding". I have no idea why this is being used, but it solves my problem. Apparently, it's a Gimp issue.
I am using Shader Model 2 and FX Composer. This is my sampler declaration;
//Colour map
texture gColourmapTexture <
string ResourceName = "Globe_Colourmap_Regions_Greyscale.png";
string ResourceType = "2D";
>;
sampler2D gColourmapSampler : register(s1) = sampler_state {
Texture = <gColourmapTexture>;
#if DIRECT3D_VERSION >= 0xa00
Filter = MIN_MAG_MIP_LINEAR;
#else /* DIRECT3D_VERSION < 0xa00 */
MinFilter = Linear;
MipFilter = Linear;
MagFilter = Linear;
#endif /* DIRECT3D_VERSION */
AddressU = Clamp;
AddressV = Clamp;
};
I never used HLSL, but I did use GLSL a while back (and I must admit it's terribly far in my head).
One issue I had with textures is that 0 is not the first pixel. 1 is not the second one. 0 is the edge of the texture and 1 is the right edge of the first pixel. The values get interpolated automatically and that can cause serious trouble if what you need is precision like when applying a lookup table rather than applying a normal texture. You need to aim for the middle of the pixel, so asking for [0.5,0.5], [1.5,0.5] rather than [0,0], [1, 0] and so on.
At least, that's the way it was in GLSL.
Beware: region in levels[region] is rounded down. When you see 5 % in your image editor, the actual value in the texture 8b representation is 5/100*255 = 12.75, which may be either 12 or 13. If it is 12, the rounding down will hit you. If you want rounding to nearest, you need to change this to levels[region+0.5].
Another similar thing (already written by Louis-Philippe) which might hit you is texture coordinates rounding rules. You always need to hit a spot in the texel so that you are not in between of two texels, otherwise the result is ill-defined (you may get any of two randomly) and some of your source texels may disapper while other duplicate. Those rules are different for bilinar and point sampling, you may need to add half of texel size when sampling to compensate for this.
GIMP uses banker's rounding. Apparently.
This threw out my code to derive region indicies.