mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 13:50:03 +00:00 
			
		
		
		
	gl_rasterizer: Fix issue with interpolation of opposite quaternions.
This commit is contained in:
		
							parent
							
								
									b694423d09
								
							
						
					
					
						commit
						8e9318f20a
					
				
					 2 changed files with 32 additions and 4 deletions
				
			
		|  | @ -158,12 +158,34 @@ void RasterizerOpenGL::Reset() { | ||||||
|     res_cache.InvalidateAll(); |     res_cache.InvalidateAll(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * This is a helper function to resolve an issue with opposite quaternions being interpolated by | ||||||
|  |  * OpenGL. See below for a detailed description of this issue (yuriks): | ||||||
|  |  * | ||||||
|  |  * For any rotation, there are two quaternions Q, and -Q, that represent the same rotation. If you | ||||||
|  |  * interpolate two quaternions that are opposite, instead of going from one rotation to another | ||||||
|  |  * using the shortest path, you'll go around the longest path. You can test if two quaternions are | ||||||
|  |  * opposite by checking if Dot(Q1, W2) < 0. In that case, you can flip either of them, therefore | ||||||
|  |  * making Dot(-Q1, W2) positive. | ||||||
|  |  * | ||||||
|  |  * NOTE: This solution corrects this issue per-vertex before passing the quaternions to OpenGL. This | ||||||
|  |  * should be correct for nearly all cases, however a more correct implementation (but less trivial | ||||||
|  |  * and perhaps unnecessary) would be to handle this per-fragment, by interpolating the quaternions | ||||||
|  |  * manually using two Lerps, and doing this correction before each Lerp. | ||||||
|  |  */ | ||||||
|  | static bool AreQuaternionsOpposite(Math::Vec4<Pica::float24> qa, Math::Vec4<Pica::float24> qb) { | ||||||
|  |     Math::Vec4f a{ qa.x.ToFloat32(), qa.y.ToFloat32(), qa.z.ToFloat32(), qa.w.ToFloat32() }; | ||||||
|  |     Math::Vec4f b{ qb.x.ToFloat32(), qb.y.ToFloat32(), qb.z.ToFloat32(), qb.w.ToFloat32() }; | ||||||
|  | 
 | ||||||
|  |     return (Math::Dot(a, b) < 0.f); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0, | void RasterizerOpenGL::AddTriangle(const Pica::Shader::OutputVertex& v0, | ||||||
|                                    const Pica::Shader::OutputVertex& v1, |                                    const Pica::Shader::OutputVertex& v1, | ||||||
|                                    const Pica::Shader::OutputVertex& v2) { |                                    const Pica::Shader::OutputVertex& v2) { | ||||||
|     vertex_batch.emplace_back(v0); |     vertex_batch.emplace_back(v0, false); | ||||||
|     vertex_batch.emplace_back(v1); |     vertex_batch.emplace_back(v1, AreQuaternionsOpposite(v0.quat, v1.quat)); | ||||||
|     vertex_batch.emplace_back(v2); |     vertex_batch.emplace_back(v2, AreQuaternionsOpposite(v0.quat, v2.quat)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void RasterizerOpenGL::DrawTriangles() { | void RasterizerOpenGL::DrawTriangles() { | ||||||
|  |  | ||||||
|  | @ -248,7 +248,7 @@ private: | ||||||
| 
 | 
 | ||||||
|     /// Structure that the hardware rendered vertices are composed of
 |     /// Structure that the hardware rendered vertices are composed of
 | ||||||
|     struct HardwareVertex { |     struct HardwareVertex { | ||||||
|         HardwareVertex(const Pica::Shader::OutputVertex& v) { |         HardwareVertex(const Pica::Shader::OutputVertex& v, bool flip_quaternion) { | ||||||
|             position[0] = v.pos.x.ToFloat32(); |             position[0] = v.pos.x.ToFloat32(); | ||||||
|             position[1] = v.pos.y.ToFloat32(); |             position[1] = v.pos.y.ToFloat32(); | ||||||
|             position[2] = v.pos.z.ToFloat32(); |             position[2] = v.pos.z.ToFloat32(); | ||||||
|  | @ -270,6 +270,12 @@ private: | ||||||
|             view[0] = v.view.x.ToFloat32(); |             view[0] = v.view.x.ToFloat32(); | ||||||
|             view[1] = v.view.y.ToFloat32(); |             view[1] = v.view.y.ToFloat32(); | ||||||
|             view[2] = v.view.z.ToFloat32(); |             view[2] = v.view.z.ToFloat32(); | ||||||
|  | 
 | ||||||
|  |             if (flip_quaternion) { | ||||||
|  |                 for (float& x : normquat) { | ||||||
|  |                     x = -x; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         GLfloat position[4]; |         GLfloat position[4]; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue