• Advertisement
Sign in to follow this  

GPU friendly compression of 2D signal

Recommended Posts

Hey

I want to try shade particles by compute a "small" number of samples, e.g. 10, in VS. I only need to compute the intensity of the light, so essentially it's a single piece of data in 2 dimensions.

Now I want to compress this data, pass it on to PS and decompress it there (the particle is a single quad and the data is passed through interpolators). I will accept a certain amount of error as long as there are no hard edges, i.e. blurred.

The compressed data has to be small and compression/decompression fast. Does anyone know of a good way to do this?

Maybe I could do something fourier based but I'm not sure of what basis functions to use.

 

Thanks

Share this post


Link to post
Share on other sites
Advertisement

If you want it only for 2D, SH is more than necessary, but i'm unsure what you try to do.

However, i use code below to calculate the curvature directions of a mesh. The problem here is that curvature direction is the same on opposite sides, e.g. vec2(0.7, 0.7) equals vec2(-0.7, -0.7) so i can not simply add vectors to get average curvature direction.

Instead i express directions with a sine wave that has two lobes pointing forwards and backwards, phase is direction and amplitude is intensity. Now adding two sine waves always results in another single sine wave and this way i get an accurate result from summing any number of samples. (Same principle is used in SH and Fourier Transform).

So, if this sounds interesting to you, you could do the same for lighting, but you would want the lobe pointing only in one direction and not the opposite as well, which means replacing factors of 2 with 1 and adjusting some other things as well.

But: For lighting i would just sum up vector wise and accept the error coming from that. Also note that my approach does not have a constant band like SH, so the same amount of light coming from right and left would result to zero - might be worth to add this for lighting.

 

 

 

	struct Sinusoid
	{
		float phase;
		float amplitude;





		Sinusoid ()
		{
			phase = 0;
			amplitude = 0;
		}

		Sinusoid (const float phase, const float amplitude)
		{
			this->phase = phase;
			this->amplitude = amplitude;
		}

		Sinusoid (const float *dir2D, const float amplitude)
		{
			this->amplitude = amplitude;
			phase = PI + atan2 (dir2D[1], dir2D[0]) * 2.0f;
		}

		float Value (const float angle) const
		{
			return cos(angle * 2.0f + phase) * amplitude;
		}

		void Add (const Sinusoid &op)
		{
			float a = amplitude;
			float b = op.amplitude;
			float p = phase;
			float q = op.phase;

			phase = atan2(a*sin(p) + b*sin(q), a*cos(p) + b*cos(q));
			float t = a*a + b*b + 2*a*b * cos(p-q);
			amplitude = sqrt(max(0,t));
		}



		float PeakAngle () const
		{
			return phase * -0.5f;
		}

		float PeakValue () const
		{
			return Value(PeakAngle ());
		}




		void Direction (float *dir2D, const float angle) const
		{
			float scale = (amplitude + Value (angle)) * 0.5f;
			dir2D[0] = sin(angle) * scale;
			dir2D[1] = cos(angle) * scale;
		}

	};

Share this post


Link to post
Share on other sites

Thanks for the replies!

SH could be an option and it did cross my mind. It's 2D indeed. The domain when using SH is usually a sphere. In my case I want to use it on a quad. Would that have an impact? I understand that spherical coords can be regarded as a square. I'm just wondering if the domain has any impact & if there would be some other basis functions more suited for an actual quad.

@JoeJ your suggestion looks similar to a fourier series which also crossed my mind. The fact that sin/cos operations are expensive on GPUs made me a little less keen. The general idea of treating the problem as some sort of curve is good though. I could use something like a power function, that could be encoded in 4 params - uv intensity multiplier & uv exponent, given that I pass on the actual colour of 1 of the corners (on the other hand this approach would only be able to depict gradients).

 I'm not 100% of how much detail I need to encode in the quad but preferably as much as possible for as little cost :)

Share this post


Link to post
Share on other sites
2 hours ago, 51mon said:

Thanks for the replies!

SH could be an option and it did cross my mind. It's 2D indeed. The domain when using SH is usually a sphere. In my case I want to use it on a quad. Would that have an impact? I understand that spherical coords can be regarded as a square. I'm just wondering if the domain has any impact & if there would be some other basis functions more suited for an actual quad.

You can warp fourier transform around a circle, and traet the circle as a square. You can then decice how much bands you need: 1st. Band is a constant term, 2nd can encode a lode towards a single directions, adding more bands means you can approximate multiple lights more accurate.

SH is similar: The smallest 2 band version has one number for the constant term (1st band), and a 3D vector (2nd band) for a directional bump. (This band tells you the dominant light direction if you gather many samples, similar to my curvature example.)

So for 2D you should need 3 numbers: Constant term, and a 2D direction (or angle and amplitude like i did, but direction avoids the trig functions when decoding).

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this  

  • Advertisement
  • Advertisement
  • Popular Tags

  • Advertisement
  • Popular Now

  • Similar Content

    • By francoisdiy
      So I wrote a programming language called C-Lesh to program games for my game maker Platformisis. It is a scripting language which tiles into the JavaScript game engine via a memory mapper using memory mapped I/O. Currently, I am porting the language as a standalone interpreter to be able to run on the PC and possibly other devices excluding the phone. The interpreter is being written in C++ so for those of you who are C++ fans you can see the different components implemented. Some background of the language and how to program in C-Lesh can be found here:

      http://www.codeloader.net/readme.html
      As I program this thing I will post code from different components and explain.
    • By isu diss
      I'm trying to duplicate vertices using std::map to be used in a vertex buffer. I don't get the correct index buffer(myInds) or vertex buffer(myVerts). I can get the index array from FBX but it differs from what I get in the following std::map code. Any help is much appreciated.
      struct FBXVTX { XMFLOAT3 Position; XMFLOAT2 TextureCoord; XMFLOAT3 Normal; }; std::map< FBXVTX, int > myVertsMap; std::vector<FBXVTX> myVerts; std::vector<int> myInds; HRESULT FBXLoader::Open(HWND hWnd, char* Filename, bool UsePositionOnly) { HRESULT hr = S_OK; if (FBXM) { FBXIOS = FbxIOSettings::Create(FBXM, IOSROOT); FBXM->SetIOSettings(FBXIOS); FBXI = FbxImporter::Create(FBXM, ""); if (!(FBXI->Initialize(Filename, -1, FBXIOS))) { hr = E_FAIL; MessageBox(hWnd, (wchar_t*)FBXI->GetStatus().GetErrorString(), TEXT("ALM"), MB_OK); } FBXS = FbxScene::Create(FBXM, "REALMS"); if (!FBXS) { hr = E_FAIL; MessageBox(hWnd, TEXT("Failed to create the scene"), TEXT("ALM"), MB_OK); } if (!(FBXI->Import(FBXS))) { hr = E_FAIL; MessageBox(hWnd, TEXT("Failed to import fbx file content into the scene"), TEXT("ALM"), MB_OK); } FbxAxisSystem OurAxisSystem = FbxAxisSystem::DirectX; FbxAxisSystem SceneAxisSystem = FBXS->GetGlobalSettings().GetAxisSystem(); if(SceneAxisSystem != OurAxisSystem) { FbxAxisSystem::DirectX.ConvertScene(FBXS); } FbxSystemUnit SceneSystemUnit = FBXS->GetGlobalSettings().GetSystemUnit(); if( SceneSystemUnit.GetScaleFactor() != 1.0 ) { FbxSystemUnit::cm.ConvertScene( FBXS ); } if (FBXI) FBXI->Destroy(); FbxNode* MainNode = FBXS->GetRootNode(); int NumKids = MainNode->GetChildCount(); FbxNode* ChildNode = NULL; for (int i=0; i<NumKids; i++) { ChildNode = MainNode->GetChild(i); FbxNodeAttribute* NodeAttribute = ChildNode->GetNodeAttribute(); if (NodeAttribute->GetAttributeType() == FbxNodeAttribute::eMesh) { FbxMesh* Mesh = ChildNode->GetMesh(); if (UsePositionOnly) { NumVertices = Mesh->GetControlPointsCount();//number of vertices MyV = new XMFLOAT3[NumVertices]; for (DWORD j = 0; j < NumVertices; j++) { FbxVector4 Vertex = Mesh->GetControlPointAt(j);//Gets the control point at the specified index. MyV[j] = XMFLOAT3((float)Vertex.mData[0], (float)Vertex.mData[1], (float)Vertex.mData[2]); } NumIndices = Mesh->GetPolygonVertexCount();//number of indices MyI = (DWORD*)Mesh->GetPolygonVertices();//index array } else { FbxLayerElementArrayTemplate<FbxVector2>* uvVertices = NULL; Mesh->GetTextureUV(&uvVertices); int idx = 0; for (int i = 0; i < Mesh->GetPolygonCount(); i++)//polygon(=mostly triangle) count { for (int j = 0; j < Mesh->GetPolygonSize(i); j++)//retrieves number of vertices in a polygon { FBXVTX myVert; int p_index = 3*i+j; int t_index = Mesh->GetTextureUVIndex(i, j); FbxVector4 Vertex = Mesh->GetControlPointAt(p_index);//Gets the control point at the specified index. myVert.Position = XMFLOAT3((float)Vertex.mData[0], (float)Vertex.mData[1], (float)Vertex.mData[2]); FbxVector4 Normal; Mesh->GetPolygonVertexNormal(i, j, Normal); myVert.Normal = XMFLOAT3((float)Normal.mData[0], (float)Normal.mData[1], (float)Normal.mData[2]); FbxVector2 uv = uvVertices->GetAt(t_index); myVert.TextureCoord = XMFLOAT2((float)uv.mData[0], (float)uv.mData[1]); if ( myVertsMap.find( myVert ) != myVertsMap.end() ) myInds.push_back( myVertsMap[ myVert ]); else { myVertsMap.insert( std::pair<FBXVTX, int> (myVert, idx ) ); myVerts.push_back(myVert); myInds.push_back(idx); idx++; } } } } } } } else { hr = E_FAIL; MessageBox(hWnd, TEXT("Failed to create the FBX Manager"), TEXT("ALM"), MB_OK); } return hr; } bool operator < ( const FBXVTX &lValue, const FBXVTX &rValue) { if (lValue.Position.x != rValue.Position.x) return(lValue.Position.x < rValue.Position.x); if (lValue.Position.y != rValue.Position.y) return(lValue.Position.y < rValue.Position.y); if (lValue.Position.z != rValue.Position.z) return(lValue.Position.z < rValue.Position.z); if (lValue.TextureCoord.x != rValue.TextureCoord.x) return(lValue.TextureCoord.x < rValue.TextureCoord.x); if (lValue.TextureCoord.y != rValue.TextureCoord.y) return(lValue.TextureCoord.y < rValue.TextureCoord.y); if (lValue.Normal.x != rValue.Normal.x) return(lValue.Normal.x < rValue.Normal.x); if (lValue.Normal.y != rValue.Normal.y) return(lValue.Normal.y < rValue.Normal.y); return(lValue.Normal.z < rValue.Normal.z); }  
    • By Descent
      Wow what a wild game by GalaXa Games Entertainment Interactive. Play now... it's really fun but IF you have epilepsy then don't play. It does not feature flashing pictures, but there is lots of animated stuff that might get ya. Anyway, 4 levels, 2 endings, insane action, BY INFERNAL. Please play it, right nao! Also , nice midi music composed by me is in the game.
       
      demons.rar
    • By Stewie.G
      Hi,
       
      I've been trying to implement a basic gaussian blur using the gaussian formula, and here is what it looks like so far:
      float gaussian(float x, float sigma)
      {
          float pi = 3.14159;
          float sigma_square = sigma * sigma;
          float a = 1 / sqrt(2 * pi*sigma_square);
          float b = exp(-((x*x) / (2 * sigma_square)));
          return a * b;
      }
      My problem is that I don't quite know what sigma should be.
      It seems that if I provide a random value for sigma, weights in my kernel won't add up to 1.
      So I ended up calling my gaussian function with sigma == 1, which gives me weights adding up to 1, but also a very subtle blur.
      Here is what my kernel looks like with sigma == 1
              [0]    0.0033238872995488885    
              [1]    0.023804742479357766    
              [2]    0.09713820127276819    
              [3]    0.22585307043511713    
              [4]    0.29920669915475656    
              [5]    0.22585307043511713    
              [6]    0.09713820127276819    
              [7]    0.023804742479357766    
              [8]    0.0033238872995488885    
       
      I would have liked it to be more "rounded" at the top, or a better spread instead of wasting [0], [1], [2] with values bellow 0.1.
      Based on my experiments, the key to this is to provide a different sigma, but if I do, my kernel values no longer adds up to 1, which results to a darker blur.
      I've found this post 
      ... which helped me a bit, but I am really confused with this the part where he divide sigma by 3.
      Can someone please explain how sigma works? How is it related to my kernel size, how can I balance my weights with different sigmas, ect...
       
      Thanks :-)
    • By cjmarsh
      When designing a game I've found that whatever the genre it can be boiled down to one thing: designing meaningful progress for the player. It sometimes seems that the MMO genre is too complex to break down in such a way but in general terms, the design seems to be about progression in key areas like Character, Exploration, Crafting, Combat, Travel, and Community. What do you think is a necessary part of an MMO's design? How would you design features for one if you were focused on increasing the player base and retaining them? What factors are especially important to consider in the design process?
  • Advertisement