Jump to content

  • Log In with Google      Sign In   
  • Create Account

InvalidPointer

Member Since 04 Apr 2007
Offline Last Active Jul 22 2016 03:59 PM

#5054727 Spherical harmonics in pixel shader

Posted by InvalidPointer on 18 April 2013 - 04:34 PM

Am I on the right track with this ? As I can see from other demos, the compute spherical harmonics per vertex ? I tend to do it in my deferred lighting pass per pixel ?

How to sample a spherical harmonics "matrix" ( a set of float values ) using my direction normal to get the illumination value at that point ? I now sample my cube maps with my direction normals, but I want to use spherical harmonics to store the data more efficiently.

I'm not an expert with SH, but here are some directions which might be helpful.

 

Spherical harmonics, like a standard sphere or a cube map, is an approximation of the incoming light from a certain direction. A cube map is really hi-quality with an according resolution. SH on the other hand is a compression. A very simple compression would be to have a direction vector and an intensity to describe from which direction the most light is coming, this case would be a 4 component vector (dirX,dirY,dirZ,intensity). This is basically, more or less (not 100% correct ?), a simple SH, which is often refered as AO term (though often only the intensity without direction is saved as single component).

 

Now to expand the idea, you can use SH, which improve the resolution of the approximation by adding more components to the vector, so a 9 component SH has a better resolution as a 4 component SH. So, some games (Halo??) use a SH X-component term and save this as lightmap (not only on vertex base) to simulate a pre-computed global illumination. But it should be clear, that you will need huge amounts of memory to save this term in a proper resolution (9 floats per pixel are not really light weight).

Hate to come off like a dick, but, uh, I'm not sure what it is you're describing. It actually sounds more like spherical radial basis function projection (which, to be fair, is pretty closely related) rather than SH, but for clarity...

 

Spherical harmonics are actually polynomials. This, by itself, isn't too interesting/useful. What is cool, however, is when we use a branch of mathematics called Fourier analysis with said polynomials as a bases. Intuitively, we're trying to describe how accurately a given spherical harmonic function mimics the source lighting information, (tl; dr, multiply the SH function by the lighting information, scale by coverage on the sphere, add into the total) then store the result as a single weight. Repeat for three color channels, and holy crap, we have a complete representation of light for all angles in 3n2 floats (where 'n' is the SH order, and is basically a 'detail/accuracy' slider). The seminal paper on the subject (that I am aware of) has some wonderful pictures on the last page that make visualizing the process really, really easy. The final piece is calculating how light from all these angles contributes in the eye direction we care about. This boils down to projecting the BRDF into SH, (remember to rotate into the same frames of reference!) then doing a dot product of those two coefficient vectors.

 

As an aside, the whole 'dot product' notation here is actually mathematically correct, but really confusing for beginners since most people tend to associate it with directions and angles. There are actually no 'directions' involved in SH, since wah waaah wahh wahh wahh frequency domain. You're just multiplying the coefficients and adding the results as you go. Pick apart a dot product, and, hey, there's the same operations.

 

EDIT: I really should write an article on this.




#5045313 Interesting effects for particle physics...

Posted by InvalidPointer on 21 March 2013 - 11:26 AM

While touched upon in the C&C presentation, Halo: Reach also has probably one of the coolest little collision tricks I've seen-- basically treat the depth buffer as a heightfield and collide against that. Details are in the presentation located at the GDC Vault and a short summary is here.




#5001563 VS 2010 is generating .lib and .exp files for a normal project, why?

Posted by InvalidPointer on 16 November 2012 - 09:54 AM

I would imagine Boost does some stuff, and it's also very likely the Microsoft CRT does some futzing too, especially if you use the run-time-linked libraries.


With that said this will have very, very negiligible impact on final compile time and you're making a mountain out of a molehill. Sometimes things need to be exported for stuff to Just Work and VIsual Studio is mature enough that basic kinks like this would have been worked out. :)


#4994017 Animated title screen?

Posted by InvalidPointer on 25 October 2012 - 09:06 PM

Nope, it's clearly not possible to accomplish. OoT/Aiydn broke reality :)

You'll need to elaborate what you mean by 'in 2D' though. If you're looking to display a sort of 'demo' run-through of a special level, that's not too hard to do; you're basically just slapping an overlay over you regular rendering pipeline. You could have an AI-controlled player character in such case, or record yourself playing something and then have the game play that back somehow-- this depends on what your technology lets you do (and the latter is rather helpful for automated testing/benchmarks, if you're into that sort of thing) and the specific effect you're looking to accomplish.


#4990718 Destructor vs Cleanup()

Posted by InvalidPointer on 16 October 2012 - 08:04 AM

yes, from C# object gets anownced that it would like to be freed (By OS manager GC). You then perform alll logic to free resources the object posesss. Like sicrane said, study cli/c++

in c++ destrocturos are not necesary, nor any good logic, from C#, managememet of memory yes

You can still design your logic to not need destructors, but your logic must be freed and managed well


I really, really hope you don't use C++ exceptions.


Why shouldn't one use exceptions in C++?


They can carry some very subtle, complicated costs and require some extra thinking when designing algorithms and classes. For games I don't really think they're worth said cost; in most cases using error codes can work equally well and can 're-enable' more dangerous (but speedier) class architectures. The latter is why I bring things up-- the C++ spec says the compiler will walk up the call stack, invoking destructors on everything until it finds an appropriate catch block. If you don't release any resources in the destructor, congratulations! You've just created a pretty massive, totally unfixable memory leak.

EDIT: That also means that using raw allocations on the stack, anywhere, is unsafe. Consider the implications. Overloading operator new can help you in limited cases, come to think of it. If you don't, though, you're in trouble.


#4990618 Destructor vs Cleanup()

Posted by InvalidPointer on 15 October 2012 - 10:41 PM

yes, from C# object gets anownced that it would like to be freed (By OS manager GC). You then perform alll logic to free resources the object posesss. Like sicrane said, study cli/c++

in c++ destrocturos are not necesary, nor any good logic, from C#, managememet of memory yes

You can still design your logic to not need destructors, but your logic must be freed and managed well


I really, really hope you don't use C++ exceptions.


#4987320 vector subscript out of bounds

Posted by InvalidPointer on 05 October 2012 - 09:46 PM

For starters you never appear to set the (extremely poorly named-- what does it do/control?) member variable 'i' to anything, so it's going to be 0xCDCDCDCD on MSVC/heap memory debugging enabled and random garbage in release mode. That's pretty big in decimal, and is very likely to be out of range when you do
void MD5Class::RenderBuffers(ID3D11DeviceContext* deviceContext)
{
    // Set vertex buffer stride and offset.
    stride = sizeof(VertexType);
    offset = 0;
	
    // Set the vertex buffer to active in the input assembler so it can be rendered.
    deviceContext->IASetVertexBuffers(0, 1, &MD5Model.subsets[i].vertBuff, &stride, &offset);
    // Set the index buffer to active in the input assembler so it can be rendered.
    deviceContext->IASetIndexBuffer(MD5Model.subsets[i].indexBuff, DXGI_FORMAT_R32_UINT, 0);

    // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
    deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);

    deviceContext->DrawIndexed(MD5Model.subsets[i].indices.size(), 0, 0);

    return;
}

EDIT: I also apologize if this comes off as snarky, but I'm not sure why you jumped to index buffer creation as being problem when the debugger explicitly tells you that you're feeding a vector an index larger than what it has room for. Slow down a bit and take the time to learn the debugger, as it's designed to make this process really straightforward. You need to walk before you can run.


#4985907 Graphics programming book recommendations

Posted by InvalidPointer on 01 October 2012 - 05:08 PM

Chiming in to recommend Real-Time Rendering. If you're into photorealistic rendering, then Physically-Based Rendering from Theory to Implementation is also a really good book-- although it's very much centered on offline rendering.

If you're just getting started, Game Engine Architecture is a good read and the chapter on animation alone is probably worth the price of the book; it was written by one of the programmers at Naughty Dog/Uncharted and has a lot of simple yet effective ideas and some practical advice.


#4980946 Delegates in AngelScript

Posted by InvalidPointer on 17 September 2012 - 11:04 AM

Currently thinking about how delegates should work, as I find myself becoming really, really hamstrung without their inclusion in AngelScript. While I think I have some of the implementation worked out, I'm curious what people would want in an implementation and what the syntax should be. From a technical perspective, I think it's best approached at a single-subscriber level, with a delegate storing a function to call and an additional 'this' pointer; multicast delegates/events could be either a library add-on extending the built-in array type or something left to the application interface to provide.

What I'm not sure of, however, is how this should interact with garbage collection (as I understand it, this bites people in the ass with startling frequency in C#, do we need to have a language-level 'weak reference' construct too?) and cross-module function imports. Community, fire away.


#4969077 How do you multithread in Directx 11?

Posted by InvalidPointer on 13 August 2012 - 08:24 AM

It takes 7 miliseconds to render a large building with 10 large directional lights and about 11 miliseconds for 60 large directional lights.I'm not very happy about the performance right now and I'm gonna optimize some more and implement instancing,but from what I understood modern engines like Frostbite 2 have both instancing and multi-threaded rendering.The thing is I have no idea how to implement multithreading,what changes do I have to make to my device and context creation?Currently I'm just using D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, &featureLevel, 1, D3D11_SDK_VERSION, &swapChainDesc, &swapChain, &DEVICE, NULL, &CONTEXT));

You aren't listening to what Hodgman is saying here. Do you know how much of that time is spent queuing up draw calls on the CPU? What he's getting at is that you may actually be GPU limited-- that is, your CPU is mostly farting around waiting for the GPU to do the work assigned to it. You'd ultimately end up making the CPU fart around even more for no actual performance gain and in fact stand to make it worse if you handle threading poorly-- many professionals still can't get this right, although that's probably more the result of mediocre teaching than any inherent difficulty.

For what it's worth, though, this and this (and even more specifically, these two methods) should help get you started.


#4967965 Template methods

Posted by InvalidPointer on 09 August 2012 - 09:21 PM

I've actually exposed asIObjectType this way to scripts and it works fairly well. The only thing I dislike is that you're (Jake) not writing idiomatic code, but with that said it's insanely flexible and can be implemented wholly with pre-implemented features.

EDIT(2): My implementation. It's not going to be a perfect cut-and-paste job, but this should get the ball rolling.
/*==================================================================*\
  AngelscriptReflection.cpp
  ------------------------------------------------------------------
  Purpose:

  ------------------------------------------------------------------
  ©2010-2012 Eldritch Entertainment, LLC.
\*==================================================================*/

//==================================================================//
// INCLUDES
//==================================================================//
#include <Scripting/Angelscript/AngelscriptReflection.hpp>
#include <Scripting/Angelscript/AngelscriptContextExtraData.hpp>
#include <Scripting/Angelscript/AngelscriptCommon.hpp>
#include <Util/Containers/UTF8String.hpp>
#include <Util/Assert.hpp>
#include <angelscript.h>
//------------------------------------------------------------------//
//==================================================================//
// LIBRARIES
//==================================================================//
ET_LINK_LIBRARY( "angelscript.lib" )
//------------------------------------------------------------------//
using namespace ::Eldritch2;
using namespace ::Eldritch2::Scripting;
using namespace ::Eldritch2::Util;
#define TYPE  "Type"
#define FUNCTION "Function"
#define MEMBER  "Member"
#define ATTRIBUTE "Attribute"
namespace
{
static const char typeName[]  = TYPE;
static const char functionName[] = FUNCTION;
static const char memberName[] = MEMBER;
static const char attributeName[] = ATTRIBUTE;
// ---------------------------------------------------
static UTF8String TypeGetNameProperty( asIObjectType* const thisPtr )
{
  AngelscriptContextExtraData* const contextExtraData = static_cast<AngelscriptContextExtraData*>( asGetActiveContext()->GetUserData() );
  return UTF8String( thisPtr->GetName(), contextExtraData->allocator );
}
// ---------------------------------------------------
static UTF8String TypeGetNamespaceProperty( asIObjectType* const thisPtr )
{
  AngelscriptContextExtraData* const contextExtraData = static_cast<AngelscriptContextExtraData*>( asGetActiveContext()->GetUserData() );
  return UTF8String( thisPtr->GetNamespace(), contextExtraData->allocator );
}
// ---------------------------------------------------
static ETNoAliasHint asIObjectType* TypeGetBaseTypeProperty( asIObjectType* const thisPtr )
{
  asIObjectType* const basePtr = thisPtr->GetBaseType();
  if( ETBranchLikelyHint( nullptr != basePtr ) )
  {
   basePtr->AddRef();
  }
  return basePtr;
}
// ---------------------------------------------------
static ETNoAliasHint asIObjectType* TypeGetSubtypeProperty( asIObjectType* const thisPtr )
{
  asIObjectType* const basePtr = thisPtr->GetSubType();
  if( ETBranchLikelyHint( nullptr != basePtr ) )
  {
   basePtr->AddRef();
  }
  return basePtr;
}
// ---------------------------------------------------
static ETNoAliasHint bool ETCDecl TypeIsInterface( asIObjectType* const thisPtr )
{
  return ( ( 0u == thisPtr->GetSize() ) & !!( asOBJ_SCRIPT_OBJECT & thisPtr->GetFlags() ) );
}
// ---------------------------------------------------
static ETNoAliasHint bool ETCDecl TypeIsFinal( asIObjectType* const thisPtr )
{
  return !!( asOBJ_NOINHERIT & thisPtr->GetFlags() );
}
// ---------------------------------------------------
static ETNoAliasHint asIScriptFunction* ETCDecl TypeGetMethodProperty( asIObjectType* const thisPtr, uint32 methodIndex )
{
  asIScriptFunction* const function = thisPtr->GetMethodByIndex( methodIndex );
  if( ETBranchLikelyHint( nullptr != function ) )
  {
   function->AddRef();
  }
  return function;
}
// ---------------------------------------------------
static ETNoAliasHint asIObjectType* ETCDecl TypeOf( void* const object, int typeID )
{
  ETUnreferencedParameter( object );
  if( asIObjectType* const result = asGetActiveContext()->GetEngine()->GetObjectTypeById( typeID ) )
  {
   result->AddRef();
   return result;
  }
  return nullptr;
}
// ---------------------------------------------------
static asIObjectType* ETCDecl GetTypeByName( const UTF8String& name )
{
  asIScriptEngine* const scriptEngine = asGetActiveContext()->GetEngine();
  AngelscriptNamespace typeNamespace;
  AngelscriptClassName typeName;
  if( ExtractClassPath( typeNamespace, typeName, name ) )
  {
   if( asIScriptModule* const scriptModule = scriptEngine->GetModule( typeNamespace ) )
   {
	asIObjectType* const objectType = scriptEngine->GetObjectTypeById( scriptModule->GetTypeIdByDecl( typeName ) );
	if( objectType )
	{
	 objectType->AddRef();
	 return objectType;
	}
   }
  }
  return nullptr;
}
// ---------------------------------------------------
static asIObjectType* ETCDecl GetTypeByName( const UTF8String& name, const UTF8String& package )
{
  asIScriptEngine* const scriptEngine = asGetActiveContext()->GetEngine();
  AngelscriptNamespace typeNamespace;
  AngelscriptClassName typeName;
  StrCpy( typeName, name );
  StrCpy( typeNamespace, package );
  if( asIScriptModule* const scriptModule = scriptEngine->GetModule( typeNamespace ) )
  {
   if( asIObjectType* const objectType = scriptEngine->GetObjectTypeById( scriptModule->GetTypeIdByDecl( typeName ) ) )
   {
	objectType->AddRef();
	return objectType;
   }
  }
  return nullptr;
}
} // anonymous namespace
namespace Eldritch2
{
namespace Scripting
{
  void RegisterReflection( asIScriptEngine* const engine )
  {
   ETRuntimeVerification( 0 <= engine->RegisterObjectType( typeName, 0u, asOBJ_REF ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectType( functionName, 0u, asOBJ_REF ) );
   // ETRuntimeVerification( 0 <= engine->RegisterObjectType( memberName, 0u, asOBJ_REF ) );
   ETRuntimeVerification( 0 <= engine->RegisterInterface( attributeName ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectBehaviour( typeName, asBEHAVE_ADDREF, "void f()",
				   asMETHOD( asIObjectType, AddRef ), asCALL_THISCALL ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectBehaviour( typeName, asBEHAVE_RELEASE, "void f()",
				   asMETHOD( asIObjectType, Release ), asCALL_THISCALL ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectBehaviour( functionName, asBEHAVE_ADDREF, "void f()",
				   asMETHOD( asIScriptFunction, AddRef ), asCALL_THISCALL ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectBehaviour( functionName, asBEHAVE_RELEASE, "void f()",
				   asMETHOD( asIScriptFunction, Release ), asCALL_THISCALL ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, TYPE "@ get_BaseType() const",
				   asFUNCTIONPR( TypeGetBaseTypeProperty, ( asIObjectType* const ), asIObjectType* ), asCALL_CDECL_OBJFIRST ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, TYPE "@ get_Subtype() const",
				   asFUNCTIONPR( TypeGetSubtypeProperty, ( asIObjectType* const ), asIObjectType* ), asCALL_CDECL_OBJFIRST ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, "string get_Name() const",
				   asFUNCTIONPR( TypeGetNameProperty, ( asIObjectType* const ), UTF8String ), asCALL_CDECL_OBJFIRST ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, "string get_Namespace() const",
				   asFUNCTIONPR( TypeGetNamespaceProperty, ( asIObjectType* const ), UTF8String ), asCALL_CDECL_OBJFIRST ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, "bool get_IsInterface() const",
				   asFUNCTIONPR( TypeIsInterface, ( asIObjectType* const ), bool ), asCALL_CDECL_OBJFIRST ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, "bool get_IsFinal() const",
				   asFUNCTIONPR( TypeIsFinal, ( asIObjectType* const ), bool ), asCALL_CDECL_OBJFIRST ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, "bool DerivesFrom( const " TYPE "@ ) const",
				   asMETHODPR( asIObjectType, DerivesFrom, ( const asIObjectType* ) const, bool ), asCALL_THISCALL ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, "bool Implements( const " TYPE "@ ) const",
				   asMETHODPR( asIObjectType, Implements, ( const asIObjectType* ) const, bool ), asCALL_THISCALL ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, "uint32 get_MethodCount() const",
				   asMETHODPR( asIObjectType, GetMethodCount, () const, asUINT ), asCALL_THISCALL ) );
   ETRuntimeVerification( 0 <= engine->RegisterObjectMethod( typeName, FUNCTION "@ get_Method( uint32 ) const",
				   asFUNCTIONPR( TypeGetMethodProperty, ( asIObjectType* const, uint32 ), asIScriptFunction* ), asCALL_CDECL_OBJFIRST ) );
   ETRuntimeVerification( 0 <= engine->RegisterGlobalFunction( TYPE "@ TypeOf( ?&in )",
				   asFUNCTIONPR( TypeOf, ( void* const, int ), asIObjectType* ), asCALL_CDECL ) );
   ETRuntimeVerification( 0 <= engine->RegisterGlobalFunction( TYPE "@ GetTypeByName( const string&in )",
				   asFUNCTIONPR( GetTypeByName, ( const UTF8String& ), asIObjectType* ), asCALL_CDECL ) );
   ETRuntimeVerification( 0 <= engine->RegisterGlobalFunction( TYPE "@ GetTypeByName( const string&in, const string&in )",
				   asFUNCTIONPR( GetTypeByName, ( const UTF8String&, const UTF8String& ), asIObjectType* ), asCALL_CDECL ) );
  }
} // namespace Scripting
} // namespace Eldritch2

EDIT 3: One thing I really, really would like to look into is attributes Like What C# Has™, on a totally unrelated note. I can think of a way to hack it together again using stock functionality, but on the subject of reflection it's really a powerful concept.


#4967341 How do I maintain a good quality real time rendering without textures?

Posted by InvalidPointer on 08 August 2012 - 04:56 AM

I have discarded the textures because our artist refuses to share his.

Sounds like it's time to get a new artist? Seriously, that's their job.


#4966361 Deferred shading ugly Phong

Posted by InvalidPointer on 05 August 2012 - 08:13 AM

Yeah, reason #3289472 why OpenGL is a design trainwreck. Remember kiddos, adding a clamp instruction is fine, but adding a special-case, higher-performance one that can be implemented in terms of the former somehow breaks hardware compatibility(???)

Good job, Khronos. You make us all so very, very proud.


#4966237 Deferred shading ugly Phong

Posted by InvalidPointer on 04 August 2012 - 08:40 PM

Phong was intentional, since it's more accurate (I am aware it's slower).


Actually Blinn-Phong produces more accurate results (try both at glancing angles and you'll see how bad Phong looks!), to even better results try Enery conserving Blinn-Phong.

QFE. The tl;dr version is that Blinn-Phong is actually an approximation to evaluating a Gaussian centered on the halfway vector.

In more plain English, you're using some statistics hacks to guess what fraction of the total surface of the area to be shaded is angled in such a way to bounce light towards you/give you laser eye surgery if it starts out coming from the light in question.

EDIT: And for extra credit, use Toksvig filtering to account for actual texture detail in the normal map!

EDIT 2: Also
float NdL = max(0.0f, dot(Normal, LightVector));
makes me really, really angry. You wouldn't like me when I'm angry. Do
float NdL = saturate(dot(Normal, LightVector));
instead to avoid my wrath.

For clarification, you're wasting precious GPU time with those max() operations that you could be getting for free with a saturate modifier. You might think that the compiler can optimize this. You'd be wrong, though-- remember that the dot product itself does not have a defined range and that the compiler generally lacks sufficient context to know that you're dotting normalized vectors.


#4963095 Register functions with default value parameters

Posted by InvalidPointer on 25 July 2012 - 04:47 PM

Default parameters are entirely a compiler thing-- they're just instructions to the compiler to add some behind-the-scenes code if you don't manually specify arguments when the function is called. Due to how Angelscript works, you can't take advantage of this since it doesn't call functions the 'normal' way. You'll need to actually add the default arguments to the AS declaration instead of just assuming the compiler will handle it.

EDIT: For clarity, I refer specifically to the "void begin(const string &in, int renderop, const string &in)" bit.




PARTNERS