http://asawicki.info/ Graphics programming, game programming, C++, games, Windows, Internet and more... 


Entries for tag "math", ordered from most recent. Entry count: 56.
21:12
Mon
27
Apr 2015
Watch out for reduced precision normalize/length in OpenGL ES
GLSL language for OpenGL ES introduces concept of precision. You can annotate variable declaration (both float and int/uint) with a precision qualifier: highp
, mediump
or lowp
, like:
mediump float a = 3.0;
You can also specify default precision qualifier by using precision statement. Language specification defines minimum required range and precision for each precision qualifier.
highp
basically means normal, singleprecision, 32bit float (IEEE 754), as we know it from CPU programming.mediump
is said to have have range of at least 2^14 ... 2^14 and relative precision 2^10, so it can be, for example, implemented using a 16bit, halfprecision float.lowp
is said to have range at least 2 ... 2 and absolute precision 2^8, so basically it can be stored as a 10bit, fixedpoint number.GPU vendors are free to use more precise data types, or even full 32bit float for all of them. What exact precision is used depends on specific GPU and maybe even operating system or graphics driver version. Using smaller data types can occupy less memory, calculate faster and consume less battery power. But it comes at the price of reduced precision and range of these numbers. Tom Olson wrote interesting articles about this: "Benchmarking floatingpoint precision in mobile GPUs": Part I, Part II, Part III.
In this post I'd like to warn you against a specific problem related to it  usage of length()
, normalize()
and distance()
functions. Using smaller data types not only limits precision in terms of number of significant digits, but also available range (over which the value will saturate to INF/+INF). For mediump, this range is defined as +/2^14, which is only 16384.
This may still look like a lot, but let's remember that calculating vector length involves intermediate value that it sum of squares of this components. This can grow very big before a square root is applied. For example, for 3D vector:
length(a) = sqrt(a.x*a.x + a.y*a.y + a.z*a.z)
If you do this operation on a mediump vector, the term a.x*a.x + a.y*a.y + a.z*a.z
can exceed maximum value for vector as small as (74.0, 74.0, 74.0). It can be very dangerous if you do something like this in your fragment shader:
precision mediump float;
uniform vec3 light_pos;
...
void main()
{
...
vec3 dir_to_light = normalize(pos  light_pos);
// Calculate your lighting and so on...
You might ask: Why isn't this intermediate value stored in high precision before taking its square root to avoid this overflow problem? Obviously it could be, as precision in any place of the shader is free to be higher than the minimum allowed in that place, so some GPU vendors can do it this way, but you shouldn't rely on this. GLSL specification clearly says that the shader is free to use same, reduced precision for intermediate values.
The precision used to internally evaluate an operation, and the precision qualification subsequently associated with any resulting intermediate values, must be at least as high as the highest precision qualification of the operands consumed by the operation.
Conclusion is: When you write shaders for OpenGL ES, watch out for operations that involve calculating vector length (or dot product) like length()
, normalize()
, distance()
, use highp
precision for vectors involved and remember that what works on one GPU due to using precision higher than minimum required, may not work on another GPU and it’s still an application issue.
Comments (4)  Tags: math opengl  Author: Adam Sawicki  Share
22:53
Thu
13
Jun 2013
FloatingPoint Formats Cheatsheet
Floatingpoint numbers (or floats in short) are not as simple as integer numbers. There is much to be understood when dealing with these numbers on low level  basic things like the sign + exponent + significand representation (and that exponent is biased, while significand has implicit leading 1), why you should never compare calculation results operator ==, that some fractions with finite decimal representation cannot be represented exactly in binary etc., as well as why there are two zeros 0 and +1, what are infinite, NaN (Not a Number) and denorm (denormal numbers) and how they behave. I won't describe it here. It's not an arcane knowledge  you can find many information about this on the Web, starting from Wikipedia article.
But after you understand these concepts, quantitative questions come to mind, like: how many significant decimal digits can we expect from precision of particular float representation (half, single, double)? What is the minimum nonzero value representable in that format? What range of integers can we represent exactly? What is the maximum value? And finally: if our game crashes with "Access violation, reading location 0x3f800000", what chances are that we mistaken pointer for a float number, as this is one of common values, meaning 1.0?
So to organize such knowledge, I created a "FloatingPoint Formats" cheatsheet:
Floats_cheatsheet.pdf
Floats_cheatsheet.docx
Comments (1)  Tags: productions math  Author: Adam Sawicki  Share
22:18
Wed
23
Jan 2013
SBX  ScaleBias Transform
A class of 2D or 3D point and vector transformations called affine transformations (that is  linear transformation plus translation) can be conveniently represented by matrix. But sometimes we don't need possibility of input x to influence output y or vice versa. For example, that's the case when we want to convert coordinates of mouse cursor from pixels, like we take it from WinAPI (where X goes right from 0 to e.g. 1280 and Y goes down from 0 to e.g. 720) to postprojective 3D space (where X goes right from 1.0 to +1.0 and Y goes up from 1.0 on the bottom to +1.0 on the top). Then every component can be transformed independently using linear transform, like this:
Parameters are: scale.xy, bias.xy.
Given input point i.xy, output point o.xy is:
o.x = i.x * scale.x + bias.x
o.y = i.y * scale.y + bias.y
This seems trivial, but what if we wanted to encapsulate such transformation in a new data structure and define operations on it, like we do on matrices? Let's call it SBX  ScaleBias Transform. I propose following structure:
struct ScaleBiasXform
{
static const ScaleBiasXform IDENTITY;
vec2 Scale;
vec2 Bias;
};
And following functions:
void Construct(ScaleBiasXform& out,
const vec2& input1, const vec2& output1,
const vec2& input2, const vec2& output2);
void Scaling(ScaleBiasXform& out, float scale);
void Scaling(ScaleBiasXform& out, const vec2& scale);
void Translation(ScaleBiasXform& out, const vec2& vec);
void Inverse(ScaleBiasXform& out, const ScaleBiasXform& sbx);
void Combine(ScaleBiasXform& out, const ScaleBiasXform& sbx1, const ScaleBiasXform& sbx2);
void TransformPoint(vec2& out, const vec2& point, const ScaleBiasXform& sbx);
void UntransformPoint(vec2& out, const vec2& point, const ScaleBiasXform& sbx);
void TransformVector(vec2& out, const vec2& vec, const ScaleBiasXform& sbx);
void UntransformVector(vec2& out, const vec2& vec, const ScaleBiasXform& sbx);
Full source code with additional comments and function bodies is here: ScaleBiasTransform.hpp, ScaleBiasTransform.cpp. Some additional notes:
Comments (1)  Tags: math  Author: Adam Sawicki  Share
22:00
Wed
12
Dec 2012
Particle System  How to Store Particle Age?
Particle effects are nice because they are simple and look interesting. Besides, coding it is fun. So I code it again :) Particle systems can have state (when parameters of each particle are calculated based on previous values and time step) or stateless (when parameters are always recalculated from scratch using fixed function and current time). My current particle system has the state.
Today a question came to my mind about how to store age of a particle to delete it after some expiration time, determined by the emitter and also unique for each particle. First, let's think for a moment about the operations we need to perform on this data. We need to: 1. increment age by time step 2. check if particle expired and should be deleted.
If that was all, the solution would be simple. It would be enough to store just one number, let's call it TimeLeft. Assigned to the particle life duration at the beginning, it would be:
Step with dt: TimeLeft = TimeLeft  dt
Delete if: TimeLeft <= 0
But what if we additionally want to determine the progress of the particle lifetime, e.g. to interpolate its color of other parameters depending on it? The progress can be expressed in seconds (or whatever time unit we use) or in percent (0..1). My first idea was to simply store two numbers, expressed in seconds: Age and MaxAge. Age would be initialized to 0 and MaxAge to particle lifetime duration. Then:
Step with dt: Age = Age + dt
Delete if: Age > MaxAge
Progress: Age
Percent progress: Age / MaxAge
Looks OK, but it involves costly division. So I came up with an idea of predividing everything here by MaxAge, thus defining new parameters: AgeNorm = Age / MaxAge (which goes from 0 to 1 during particle lifetime) and AgeIncrement = 1 / MaxAge. Then it gives:
Step with dt: AgeNorm = AgeNorm + dt * AgeIncrement
Delete if: AgeNorm > 1
Progress: Age / AgeIncrement
Percent progress: AgeNorm
This needs additional multiplication during time step and division when we want to determine progress in seconds. But as I consider progress in percent more useful than the absolute progress value in seconds, that's my solution of choice for now.
Comments (6)  Tags: math rendering  Author: Adam Sawicki  Share
22:06
Sun
15
Apr 2012
DirectXMath  A First Look
Programmers using DirectX have access to the accompanying library full of routines that support various 3D math calculations. First it was D3DX  auxilliary library tightly coupled with DirectX itself. It's still there, but some time ago Microsoft released a new library called XNA Math. I've written about it here. This library also ships with latest versions of DirectX SDK (which was last updated in Jun 2010), but it is something completely new  a more independent, small, elegant and efficient library, portable between Windows (where it can use SSE2 or old FPU) and Xbox 360.
Now Microsoft comes up with another library called DirectXMath. See the official documentation and introductory blog post for details. As a successor of XNA Math, it looks very similar. Main differences are:
Sounds great? Well, I didn't tell yet how to get it. It's not shipped with DirectX SDK. You need the SDK for Windows 8. They say that:
The library is annotated using SAL2 (rather than the older VSstyle or Windowsstyle annotations), so requires the standalone Windows SDK for Windows 8 Consumer Preview or some additional fixups to use with Visual C++ 2010.
So probably I'm not going to switch to this new library anytime soon :P
Comments (8)  Tags: directx math  Author: Adam Sawicki  Share
19:58
Sat
17
Mar 2012
Ball Physics
Checking collision between different kinds of 2D or 3D shapes is a subject I deal with for some time. It is useful in game development to determine if some object is visible or affected by light and this if it should be rendered. I have lots of function to check such collisions in the math module of CommonLib library.
But that is only the beginning if you want to make physics. Adding physical behavior to your game requires additional calculations to correct positions and apply forces to colliding bodies. I prepared a small code snippet that implements physical 2D collision between moving circle and another moving circle (calculated by CircleToCircleCollision function), static line (CircleToLineCollision function) and static axisaligned rectangle (CircleToRectangleCollision function).
Download: ball_physics.cpp
The code uses some good practices (like fixed time step) as well as bad practices (like Euler integration). It should be enough for a simple physics in game like a platformer.
Comments (5)  Tags: math physics  Author: Adam Sawicki  Share
21:58
Thu
29
Sep 2011
Vector Register Size  Diagram
It may be hard to imagine and remember what is the exact number of bits, bytes, words or floats in some piece of data, like a SIMD register. So today I've made following diagram/cheatsheet:
Here you can find its "source" in OpenOffice Draw format: Vector_register_size.odg.
Comments (7)  Tags: hardware math  Author: Adam Sawicki  Share
20:45
Sun
24
Jul 2011
DevMeeting and Demoscene Night
Yesterday I attended two events related to my interests. First was about collision detection in JavaScript games, organized by Marek Paw³owski from devmeetings.pl. During this almost 12hour workshop we could learn about JavaScript, as well as some theory of 2D collision detection and space partitioning techniques. Nothing new for me, but I liked formula of this workshop. We could learn some theory from slides, but most of the time we had some tasks to code on our laptops  first to implement a simple library for aspects in JavaScript and then to code Asteroids game, including precise collision detection and QuadTree. Most of the code for this game was ready  we were given a framework, as well as libraries with math and a class for QuadTree. Our task was to just to connect it together. I think it's a good way of teaching programming in practice.
Web technologies are not my main interest, but I like JavaScript. I even think it's the most beautiful scripting language in terms of syntax. Being able to freely draw 2D (using HTML Canvas) as well as 3D graphics (using WebGL) makes it a good technology for learning and prototyping geometry or gameplay algorithms. Marek also pointed on the DevMeeting an interesting conclusion that it's very easy to port algorithms from C/C++ to JavaScript. But on the other hand, Dab reminded me today that Lua is a scripting language with very similar features, but faster and more lightweight interpreter, so it's still better choice as a scripting language embedded in highperformance software like games.
Second event was Noc z Demoscen±  Demoscene Night. It took place in Fabryka Kot³ów club (former No Mercy). During that night we could see some olschool, as well as newschool demos presented on a a big screen and, what is most important, meet some nice people there. Thanks Voyager for organizing this event! It was great especially because the 3day demoscene party RiverWash, which took place here in Warsaw in last 2 years, this year is in £ód¼ and I can't attend it. This night was very inspiring and now I feel like developing a technology to make a demo :)
Comments (1)  Tags: demoscene webdev javascript math events  Author: Adam Sawicki  Share
[Stat] [Admin] [STAT NO AD] [pub] [Mirror]  Copyright © 20042016 Adam Sawicki 