Noise Texture should use no filtering [Resolved]

I’m getting a Shader create error on my favorite Vista 32-bit testing box with an “NVIDIA GeForce 7350 LE” and I just installed “181.22_geforce_winvista_32bit_english_whql.exe”.

I’ll attach the player log and the suspect shader.

117263–4444–$log_05844938506f474c8c09f2148fb843b2_214.txt (57.9 KB)
117263–4445–$foursplatblendwrapshader2_152.shader (3.38 KB)

Pixel shader 3.0 does not allow doing ordinary texture lookups inside dynamic branches (why? because it won’t be able to compute derivatives of the texture coordiantes). If you want to do a texture lookup, you have to provide your own derivatives.

I’m not sure what your shader is supposed to do, but it’s awfully long (close to 300 instructions!). Maybe you should approach what you’re trying to do in some other way?

I updated the shader above to 185 from 238.

Essentially what this shader is doing is blending 4 splats into a single texture.

I added a wrapping component via TEXCOORD1. I’m using wrap 2 here for visual simplicity. 8 wraps has more orientation variation.

I also added a random texture lookup to randomly orient the splats so that the tiles didn’t all face the same direction.

The wrapped splats use the vertex position offset to select the random noise uvs.

I’ll try a little test project to see if it works with the reduced instructions. And then I’ll post it.

First testing on windows…

Weird it sorta worked on my Vista 32-bit machine this time. But visually, this is not what I was expecting.

The screenshot above was via the Mac web player.

This screenshot below is from Vista.

Okay here is a little test project with the shader.

SplatGameObject2 is the object on the right. Use wasd and arrow keys to get over there. Click drag for mouse look.

117296--4447--$sorta_worked_859.jpg
117296–4448–$relieftest005_189.zip (4.37 MB)

I must’ve been hitting an instruction limit. In my orientation if checks, I changed the logic to set the uv only. I moved the function calls to get the splat colors after the if blocks, which saved on instructions. I added the debug if statements after that. Using color is the only way I can debug these Unity shaders for now.

Anyway, just doing that caused the shader to compile on the Vista box. So it must’ve been the instruction count.

117298--4449--$works_561.jpg

I’m still not sure what accounts for the miserably looking difference between the mac and Vista version. One has numbers and one looks like puke when comparing the web players.

Is it something like the JPG has been compressed? This almost looks diffuse or like a compression pattern. So maybe the Mac allows runtime decompression, and maybe my Vista graphics card does not?

Web player link - Unity Web Player - ReliefTest004

It’s definitely not something with the textures.

My guess is that you’re using some shader construct that is undefined in principle. So it might work on one shader API but not another, or on one graphics card but not another. Since I still don’t quite understand what you’re trying to achieve and why the shader is so long complex, I’m just talking out of thin air :slight_smile:

If it’s not some bug in the shader, then you might be hitting some bug in Cg compiler, where it compiles the shader wrong for D3D. I did hit that twice over three years, so there is a non zero chance.

Well I’ll explain how the shader works and then try initializing some variables.

The following is done in a single pass.

uniform sampler2D _MainTex;

The main diffuse texture is a 4x4 splat 512x512 texture with 16 splats. Each splat is 128x128.

uniform sampler2D _RandTex;

The random texture is a 512x512 noise texture I generated in Gimp.

struct appdata {
    float4 vertex : POSITION;
    float4 texcoord : TEXCOORD0;
    float4 texcoord1 : TEXCOORD1;
    float4 color : COLOR;
};

This is the vertex program input.

POSITION is vertex positions currently ranging from (-1000, 0, -1000) to (3000, 0, 3000).

TEXCOORD0 are barycentric uv coordinates all with the same orientation. This is used in the splat blending process.

TEXCOORD1 is the wrap component. Values (1.0, 1.0) are no wrapping. Values (2.0, 4.0) would be wrap twice about the X and wrap 4 times about the Z.

COLOR indicates the splat index. The R channel is 1 of 16 splats for the top left. The G channel is the top right. The B channel is bottom left. The A channel is bottom right.

struct v2f {
    float4 pos : POSITION;
    float4 texcoord : TEXCOORD0;
    float4 texcoord1 : TEXCOORD1;
    float4 color : COLOR;
    int offsetX;
    int offsetZ;
};

This is the vertex program output and the fragment program input.

v2f vert (appdata v) {
    v2f o;
    o.pos = mul( glstate.matrix.mvp, v.vertex);
    o.texcoord = v.texcoord;
    o.texcoord1 = v.texcoord1;
    o.color = v.color;
    o.offsetX = int(abs(v.vertex.x)) % 512;
    o.offsetZ = int(abs(v.vertex.z)) % 512;
    return o;
}

Start of vertex program.

The position is converted to world space.

TEXCOORD, TEXCOORD1, COLOR are just passed down the pipeline.

OffsetX is an int value. The vertex position X value is being wrapped every 512 units.

OffsetZ is an int value. The vertex position Z value is being wrapped every 512 units.

End of vertex program.

float GetScaledRemainder(float k, float scaler)
{
	k *= scaler;
	int reduction = k;
	return k-reduction;
}

This is a helper function to scale a uv component and remove the integer value. I use these exclusively to wrap the uvs and cause them to repeat over the 0 to 1 range.

float4 GetTileWrapColor(float height, float intensity, float2 uvs, int wrapX, int wrapY)
{
	int index = 15.99 * height;
	int col = index % 4; // 0 to 3
    int row = height * 3.99; // to 0 to 3
    
    uvs.x = (col+GetScaledRemainder(uvs.x, wrapX))*0.25;
    uvs.y = (row+GetScaledRemainder(uvs.y, wrapY))*0.25;

    float4 color = tex2D(_MainTex, uvs);    
    return lerp(float4(0,0,0,0), color, intensity);
}

This is the splat blending process.

The height determines which splat to select of the 16 splats. Valid index values are 0 to 15. The column and row indicate which splat to select. Valid columns values are 0 to 3. Valid row values are 0 to 3. The uvs are adjusted to select the splat and the color is sampled from the diffuse texture. The intensity is used to cause a gradient using the uvs. The color is lerped from black so that I can use additive blending.

float4 frag(v2f i) : COLOR
{
	//define the final splat color
	float4 final;
	
	//define the splat colors
	float4 tile1Color;
	float4 tile2Color;
	float4 tile3Color;
	float4 tile4Color;

Start of the fragment program.

I have the tile colors defined as such so I can debug how each splat is blended.

	//get the wrap component
	int wrapX = i.texcoord1.x;
	int wrapY = i.texcoord1.y;

I grab the wrap components from TEXCOORD1. I only want the integer values so that the splat is tiled edge to edge.

	i.texcoord.x * wrapX;
	i.texcoord.y * wrapY;

This selects which subsplat is being wrapped. Given a wrap component of 2, valid values are in the 0, 1 or 2 integers.

	// use the position offset to select the uvs from the random texture while also wrapping
	int orientation =
		tex2D(_RandTex, float2(
		i.offsetX / 512.0 + int(i.texcoord.x*wrapX)/float(wrapX),
		i.offsetZ / 512.0 + int(i.texcoord.y*wrapY)/float(wrapY)
		)).x * 8;

A few things are happening here. The random noise texture is being sampled. I’m only using the Red channel of the texture. The value is being multipled by 8. Expected values are 0, 1, 2, 3, 4, 5, 6, 7, 8(rarely).

The position offset from the vertex program is being divided by 512. That gives a range of 0 to 1. The offset is being used to slide the uvs along the random noise texture.

The noise lookup is being staggered so that the same noise is being used for each subsplat. Otherwise I’d have a noise pattern within the splat blending pattern.

	// use 4 orientations
	orientation = orientation % 4;

Select between 4 orientations each rotated by 90 degrees.

	//get the splat colors
	float2 uv;
	if (orientation == 0)
	{
		uv = float2(i.texcoord.x, i.texcoord.y);
	}

	else if (orientation == 1)
	{
		uv = float2(i.texcoord.y, 1-i.texcoord.x);
	}
	
	else if (orientation == 2)
	{
		uv = float2(1-i.texcoord.x, 1-i.texcoord.y);
	}
	
	else //if (orientation == 3)
	{
		uv = float2(1-i.texcoord.y, i.texcoord.x);
	}

Each orientation rotates the uvs by 90 degrees. These are the uvs used to lookup the diffuse color of the subsplats.

	tile1Color = GetTileWrapColor(i.color.r, (1-i.texcoord.x) * (1-i.texcoord.y), uv, wrapX, wrapY);
	tile2Color = GetTileWrapColor(i.color.g, i.texcoord.x * (1-i.texcoord.y), uv, wrapX, wrapY);
	tile3Color = GetTileWrapColor(i.color.b, (1-i.texcoord.x) * i.texcoord.y, uv, wrapX, wrapY);
	tile4Color = GetTileWrapColor(i.color.a, i.texcoord.x * i.texcoord.y, uv, wrapX, wrapY);

The COLOR component selects with splat to use for topleft, topright, bottomleft, and bottomright.

The TEXCOORD component is used to specify the intensity of the splats to blend the splats with black so that additive blending can be used.

The UV specifies the texture coordinate and orientation for the diffuse color lookup. The wrapX and wrapY components specify how many times to wrap the subsplat.

	final = tile1Color + tile2Color + tile3Color + tile4Color;

Combine the 4 splats together with additive blending.

	// apply a color for debugging
	const float debug = 0;

Debug is used to color each subsplat so I can see which noise values were selected. I change the value to 1 so I can see each subsplat orientation as a primary color.

	if (orientation == 0)
	{
		final.r += debug;
	}

	else if (orientation == 1)
	{
		final.g += debug;
	}
	
	else if (orientation == 2)
	{
		final.b += debug;
	}
	
	else if (orientation == 3)
	{
		final.r += debug;
		final.g += debug;
	}

The debug color alterations are made to show what orientation was used.

	return final;

The final color is returned.

I can save one fragment program instruction by transfering TEXCOORD1 to the wrap components’ integer in the vertex program.

I’m seeing one oddity however. The order of int values in the vertex program output is changing the final output.

struct v2f {
    float4 pos : POSITION;
    float4 texcoord : TEXCOORD0;
    float4 texcoord1 : TEXCOORD1;
    float4 color : COLOR;
    int wrapX;
    int wrapZ;
    int offsetX;
    int offsetZ;
};

Wrap component is first. Test Unity Web Player - ReliefTest005

struct v2f {
    float4 pos : POSITION;
    float4 texcoord : TEXCOORD0;
    float4 texcoord1 : TEXCOORD1;
    float4 color : COLOR;
    int offsetX;
    int offsetZ;
    int wrapX;
    int wrapZ;
};

Offset component is first. Unity Web Player - ReliefTest006

There definitely appears to be some kind of problem. I’ve used other shader engines before like Ogre3d, and there appears to be a problem in either the shader parameter index, or shader parameter name.

Mac 5 - Is the Mac with ReliefTest005 in the web player
Mac 6 - Is the Mac with ReliefTest006 in the web player
Vista 5 - Is Vista with ReliefTest005 in the web player
Vista 6 - Is Vista with ReliefTest006 in the web player

Ideas???

117486–4454–$relieftest006_135.zip (4.37 MB)
117486--4455--$comparison_162.gif
117486--4457--$shader_parameter_order_545.gif

I’m moving this to ShaderLab in 3… 2… 1…

I’ve tested the 004 one

and it is broken on both machines.

On mac it might slightly show some kind of numbers but it has massive diagonal interferance if you look in from edge to middle (“45°”) and if your move the cam towards it in that angle, it leads to verticaly moving disortion.

if you are looking in at a different angle it is just a mess with some “probabilistic weight” around the numbers.

(005 and 006 are totally broken on both)

Both machines means:
MacBook Pro 2.4Ghz, 8600M GT 256MB
Vista64, Core i7 920, GTX 280 @ 181.20

Ok, here are some ideas:

  1. in v2f structure, you either need to explicitly add all TEXCOORDn semantics, or leave all of them out (for the Cg compiler to assign). So either label everything, or just label POSITION and COLOR.

  2. Shader Model 3.0 does not have integer data types. Cg probably generates tons of code to make them “sort of work”, but if you can, try using some other approach. This is more of a performance issue, I guess.

I’m still not sure what your shader is trying to do. You explained what it does in all the steps, but can you tell it’s purpose in three simple sentences? So far I have a feeling that you’re trying to do something very complex very slow for no good reason :slight_smile: (my feeling could very well be wrong)

  1. I don’t want to specify a semantic because the result would get interpolated. I don’t want the value to be interpolated. If I use float instead of int my instructions drop from 185 to 122.

  2. This shader does a few things. A) Blends four splats together B) Tiles each splat several times within a quad based on the multiple in TEXCOORD1 C) Randomly orients each subtile.

Here’s another way to think about it. Say you have a texture sheet of 16 splats. How do you apply the splat to a quad? Each quad would get a fixed uv coordinate. If you attempt to scale the uv, the texture would bleed into the next splat.

Or like the picture below. Each corner of the quad is assigned a splat. The splat is tiles N times multiplied by the gradient and added together.

There’s no way to turn interpolation off for several things across the triangle. Everything always gets interpolated. If you don’t want interpolation, the only way is to make sure all three vertices of the triangle produce the same result (it will still be interpolated, but will result in the same value everywhere).

…I still don’t quite understand why you want to do that. Sure, it’s possible, but it’s pretty much destined to be very slow. Why would you want that?

Why?

  1. I want to fake that I have more polygons than I really do.

  2. I want to add detail without adding polygons.

  3. I have too many polygons already and I’m trying to eliminate as many as I can.

  4. I assume that wrapping the texture on a polygon is faster than drawing an additional polygon.

  5. I have a mesh with the maximum amount of polygons. I wrapped each polygon 8 times and I still see 30 FPS. In actual use, I plan to have much fewer polygons.

What?

  1. Frame 1 - Texture sheet has a grid of splats. Scaling of uvs won’t work to tile because that would bleed into the next splat.

  2. Frame 2 - Highlighted in red is the polygon I want to remove.

  3. Frame 3 - The green polygon replaced 4 other polygons.

  4. Frame 4 - Select more polygons to be replaced

  5. Frame 5 - Replaced 5 polygons with 2 polygons

(more on page 2)

117701--4459--$shader_concept_747.gif

I converted the int fragment parameters to use TEXCOORD semantics.

Hmmm. The Vista vertex : POSITION must be a different scale than the Mac vertex : POSITION scale.

I added a debug slider to show the orientations of the sub splats.

http://tagenigma.com/qa/Unity3d/ReliefTest007/ReliefTest007.html

This would only happen if the vertex : POSITION was not in the 0 to 1000 scale.

Here is what it looks like on the Mac (picture of expected grids)

Here is what it looks like on Vista: (the noisy color picture)

Just slide the debug slider to the right (1.0)



117742–4463–$relieftest007_174.zip (4.38 MB)

I ported the shader to render monkey for experimentation. I had all kinds of problems with getting a supported format that supports two sets of uv coords. In the end I hardcoded the wrap in the Vertex Program for testing.

I’ve attached the RenderMonkey project if someone is feeling adventurous which includes the blasted .X file.

I see the same issue in RenderMonkey, however. I’ve narrowed the issue down to this little bit of code.

    o.texcoord2 = float2(
       (int(abs(v.vertex.x)) % 512),
       (int(abs(v.vertex.z)) % 512)
       );

My .X file specifies verteces in the (-1000, 0 -1000) to (1000, 0, 1000) range. Now I am assuming that I can modulate this range to 0 to 511. However, I am uncertain to the scale of the incoming verteces. If the scale was abnormally large, this would give the appearance of being noise. In that case I would scale the verteces down with an

int(abs(v.vertex.X / k)) % 512

What is the expected range for a vertex? And why is the range apparently different on Windows and Mac?

118061–4474–$rendermonkeysplatblendwrap_206.zip (8 KB)

If I normalize the vertex position that seems to put the data into an acceptable range.

    o.texcoord2 = normalize(v.vertex).xz;
    o.texcoord2 = float2(
       int(abs(o.texcoord2.x)),
       int(abs(o.texcoord2.y))
       );


118078–4477–$rendermonkeysplatblendwrap_457.zip (8.55 KB)

The vertex size seems to correspond to the vertex positions inside my .x file. That’s what I’d expect.

In that case, I’d want the noise orientations to correspond to the quad size in the .x file. This will work if I wrap once or wrap 8 times to see pseudo random orientations.

    const float halfQuadSize = 500.0f;    
    o.texcoord2.x = int(abs((v.vertex.x)/halfQuadSize)) + int(_MoveXZ.x);
    o.texcoord2.y = int(abs((v.vertex.z)/halfQuadSize)) + int(_MoveXZ.y);


118191–4479–$rendermonkeysplatblendwrap_348.zip (206 KB)

In my Unity project I have quad sizes of 62.5.

    const float halfQuadSize = 31.25;
    o.texcoord2.x = int(abs((v.vertex.x)/halfQuadSize)) + int(_MoveXZ.x);
    o.texcoord2.y = int(abs((v.vertex.z)/halfQuadSize)) + int(_MoveXZ.y);

But the problems I talked about before are hampering me. Specifically the scale of v.vertex. It seems like the vertex scale is platform specific. How can I get the scale to be the same on Mac and Windows? I can’t just normalize the v.vertex this time, because I need the values to be in a specific range.

Is there something that can be done under the hood?