Selaa lähdekoodia

Merge pull request #83 from jsarrett/unified_shader

Unified shader
TheOnlyJoey 8 vuotta sitten
vanhempi
commit
538a0462f6

+ 1 - 0
CMakeLists.txt

@@ -24,6 +24,7 @@ set(openhmd_source_files
 	${CMAKE_CURRENT_LIST_DIR}/src/platform-posix.c
 	${CMAKE_CURRENT_LIST_DIR}/src/fusion.c
 	${CMAKE_CURRENT_LIST_DIR}/src/queue.c
+	${CMAKE_CURRENT_LIST_DIR}/src/shaders.c
 )
 
 OPTION(OPENHMD_DRIVER_OCULUS_RIFT "Oculus Rift DK1 and DK2" ON)

+ 74 - 8
examples/opengl/main.c

@@ -13,8 +13,12 @@
 #include <math.h>
 #include "gl.h"
 
-#define TEST_WIDTH 1280
-#define TEST_HEIGHT 800
+/* DK1 */
+//#define TEST_WIDTH 1280
+//#define TEST_HEIGHT 800
+/* DK2 */
+#define TEST_WIDTH 1920
+#define TEST_HEIGHT 1080
 
 #define EYE_WIDTH (TEST_WIDTH / 2 * 2)
 #define EYE_HEIGHT (TEST_HEIGHT * 2)
@@ -80,6 +84,18 @@ void draw_scene(GLuint list)
 	// draw cubes
 	glCallList(list);
 }
+static inline void
+print_matrix(float m[])
+{
+    printf("[[%0.4f, %0.4f, %0.4f, %0.4f],\n"
+            "[%0.4f, %0.4f, %0.4f, %0.4f],\n"
+            "[%0.4f, %0.4f, %0.4f, %0.4f],\n"
+            "[%0.4f, %0.4f, %0.4f, %0.4f]]\n",
+            m[0], m[4], m[8], m[12],
+            m[1], m[5], m[9], m[13],
+            m[2], m[6], m[10], m[14],
+            m[3], m[7], m[11], m[15]);
+}
 
 int main(int argc, char** argv)
 {
@@ -99,9 +115,30 @@ int main(int argc, char** argv)
 	ohmd_device_settings_seti(settings, OHMD_IDS_AUTOMATIC_UPDATE, &auto_update);
 
 	ohmd_device* hmd = ohmd_list_open_device_s(ctx, 0, settings);
+	float viewport_scale[2];
+	float distortion_coeffs[4];
+	float aberr_scale[3];
+	float sep;
+	float left_lens_center[2];
+	float right_lens_center[2];
+	//viewport is half the screen
+	ohmd_device_getf(hmd, OHMD_SCREEN_HORIZONTAL_SIZE, &(viewport_scale[0]));
+	viewport_scale[0] /= 2.0f;
+	ohmd_device_getf(hmd, OHMD_SCREEN_VERTICAL_SIZE, &(viewport_scale[1]));
+	//distortion coefficients
+	ohmd_device_getf(hmd, OHMD_UNIVERSAL_DISTORTION_K, &(distortion_coeffs[0]));
+	ohmd_device_getf(hmd, OHMD_UNIVERSAL_ABERRATION_K, &(aberr_scale[0]));
+	//calculate lens centers (assuming the eye separation is the distance betweenteh lense centers)
+	ohmd_device_getf(hmd, OHMD_LENS_HORIZONTAL_SEPARATION, &sep);
+	ohmd_device_getf(hmd, OHMD_LENS_VERTICAL_POSITION, &(left_lens_center[1]));
+	ohmd_device_getf(hmd, OHMD_LENS_VERTICAL_POSITION, &(right_lens_center[1]));
+	left_lens_center[0] = viewport_scale[0] - sep/2.0f;
+	right_lens_center[0] = sep/2.0f;
+	//asume calibration was for lens view to which ever edge of screen is further away from lens center
+	float warp_scale = (left_lens_center[0] > right_lens_center[0]) ? left_lens_center[0] : right_lens_center[0];
 
 	ohmd_device_settings_destroy(settings);
-	
+
 	if(!hmd){
 		printf("failed to open device: %s\n", ohmd_ctx_get_error(ctx));
 		return 1;
@@ -112,12 +149,18 @@ int main(int argc, char** argv)
 
 	SDL_ShowCursor(SDL_DISABLE);
 
-	char* vertex = read_file("shaders/test1.vert.glsl");
-	char* fragment = read_file("shaders/test1.frag.glsl");
+	const char* vertex;
+	ohmd_gets(OHMD_GLSL_DISTORTION_VERT_SRC, &vertex);
+	const char* fragment;
+	ohmd_gets(OHMD_GLSL_DISTORTION_FRAG_SRC, &fragment);
 
 	GLuint shader = compile_shader(vertex, fragment);
 	glUseProgram(shader);
 	glUniform1i(glGetUniformLocation(shader, "warpTexture"), 0);
+	glUniform2fv(glGetUniformLocation(shader, "ViewportScale"), 1, viewport_scale);
+	glUniform1f(glGetUniformLocation(shader, "WarpScale"), warp_scale);
+	glUniform4fv(glGetUniformLocation(shader, "HmdWarpParam"), 1, distortion_coeffs);
+	glUniform3fv(glGetUniformLocation(shader, "aberr"), 1, aberr_scale);
 	glUseProgram(0);
 
 	GLuint list = gen_cubes();
@@ -151,6 +194,29 @@ int main(int argc, char** argv)
 						ohmd_device_setf(hmd, OHMD_POSITION_VECTOR, zero);
 					}
 					break;
+				case SDLK_F3:
+					{
+						float mat[16];
+						ohmd_device_getf(hmd, OHMD_LEFT_EYE_GL_PROJECTION_MATRIX, mat);
+						printf("Projection L: ");
+						print_matrix(mat);
+						printf("\n");
+						ohmd_device_getf(hmd, OHMD_RIGHT_EYE_GL_PROJECTION_MATRIX, mat);
+						printf("Projection R: ");
+						print_matrix(mat);
+						printf("\n");
+						ohmd_device_getf(hmd, OHMD_LEFT_EYE_GL_MODELVIEW_MATRIX, mat);
+						printf("View: ");
+						print_matrix(mat);
+						printf("\n");
+						printf("viewport_scale: [%0.4f, %0.4f]\n", viewport_scale[0], viewport_scale[1]);
+						printf("warp_scale: %0.4f\r\n", warp_scale);
+						printf("distoriton coeffs: [%0.4f, %0.4f, %0.4f, %0.4f]\n", distortion_coeffs[0], distortion_coeffs[1], distortion_coeffs[2], distortion_coeffs[3]);
+						printf("aberration coeffs: [%0.4f, %0.4f, %0.4f]\n", aberr_scale[0], aberr_scale[1], aberr_scale[2]);
+						printf("left_lens_center: [%0.4f, %0.4f]\n", left_lens_center[0], left_lens_center[1]);
+						printf("right_lens_center: [%0.4f, %0.4f]\n", left_lens_center[0], left_lens_center[1]);
+					}
+					break;
 				default:
 					break;
 				}
@@ -174,6 +240,7 @@ int main(int argc, char** argv)
 		// Draw scene into framebuffer.
 		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, left_fbo);
 		glViewport(0, 0, EYE_WIDTH, EYE_HEIGHT);
+		glClearColor(0.0, 0.0, 0.0, 1.0);
 		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
 		draw_scene(list);
 		glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
@@ -213,6 +280,7 @@ int main(int argc, char** argv)
 		glLoadIdentity();
 
 		// Draw left eye
+		glUniform2fv(glGetUniformLocation(shader, "LensCenter"), 1, left_lens_center);
 		glBindTexture(GL_TEXTURE_2D, left_color_tex);
 		glBegin(GL_QUADS);
 		glTexCoord2d( 0,  0);
@@ -226,6 +294,7 @@ int main(int argc, char** argv)
 		glEnd();
 
 		// Draw right eye
+		glUniform2fv(glGetUniformLocation(shader, "LensCenter"), 1, right_lens_center);
 		glBindTexture(GL_TEXTURE_2D, right_color_tex);
 		glBegin(GL_QUADS);
 		glTexCoord2d( 0,  0);
@@ -250,8 +319,5 @@ int main(int argc, char** argv)
 
 	ohmd_ctx_destroy(ctx);
 
-	free(vertex);
-	free(fragment);
-	
 	return 0;
 }

+ 0 - 43
examples/opengl/shaders/test1.frag.glsl

@@ -1,43 +0,0 @@
-#version 120
-
-// Taken from mts3d forums, from user fredrik.
-
-uniform sampler2D warpTexture;
-
-const vec2 LeftLensCenter = vec2(0.2863248, 0.5);
-const vec2 RightLensCenter = vec2(0.7136753, 0.5);
-const vec2 LeftScreenCenter = vec2(0.25, 0.5);
-const vec2 RightScreenCenter = vec2(0.75, 0.5);
-const vec2 Scale = vec2(0.1469278, 0.2350845);
-const vec2 ScaleIn = vec2(4, 2.5);
-const vec4 HmdWarpParam   = vec4(1, 0.22, 0.24, 0);
-
-// Scales input texture coordinates for distortion.
-vec2 HmdWarp(vec2 in01, vec2 LensCenter)
-{
-	vec2 theta = (in01 - LensCenter) * ScaleIn; // Scales to [-1, 1]
-	float rSq = theta.x * theta.x + theta.y * theta.y;
-	vec2 rvector = theta * (HmdWarpParam.x + HmdWarpParam.y * rSq +
-		HmdWarpParam.z * rSq * rSq +
-		HmdWarpParam.w * rSq * rSq * rSq);
-	return LensCenter + Scale * rvector;
-}
-
-void main()
-{
-	// The following two variables need to be set per eye
-	vec2 LensCenter = gl_FragCoord.x < 640 ? LeftLensCenter : RightLensCenter;
-	vec2 ScreenCenter = gl_FragCoord.x < 640 ? LeftScreenCenter : RightScreenCenter;
-
-	vec2 oTexCoord = gl_FragCoord.xy / vec2(1280, 800);
-
-	vec2 tc = HmdWarp(oTexCoord, LensCenter);
-	if (any(bvec2(clamp(tc,ScreenCenter-vec2(0.25,0.5), ScreenCenter+vec2(0.25,0.5)) - tc)))
-	{
-		gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
-		return;
-	}
-
-	tc.x = gl_FragCoord.x < 640 ? (2.0 * tc.x) : (2.0 * (tc.x - 0.5));
-	gl_FragColor = texture2D(warpTexture, tc);
-}

+ 0 - 8
examples/opengl/shaders/test1.vert.glsl

@@ -1,8 +0,0 @@
-#version 120
-
-void main(void)
-{
-	gl_TexCoord[0] = gl_MultiTexCoord0;
-	gl_Position = ftransform();
-}
-

+ 25 - 1
include/openhmd.h

@@ -57,6 +57,12 @@ typedef enum {
 	OHMD_PATH      = 2,
 } ohmd_string_value;
 
+/** A collection of string descriptions, used for getting strings with ohmd_gets(). */
+typedef enum {
+	OHMD_GLSL_DISTORTION_VERT_SRC = 0,
+	OHMD_GLSL_DISTORTION_FRAG_SRC = 1,
+} ohmd_string_description;
+
 /** A collection of float value information types, used for getting and setting information with
     ohmd_device_getf() and ohmd_device_setf(). */
 typedef enum {
@@ -109,7 +115,7 @@ typedef enum {
 
 	/** float[6] (get): Device specific distortion value. */
 	OHMD_DISTORTION_K                     = 18,
-	
+
 	/**
 	 * float[10] (set): Perform sensor fusion on values from external sensors.
 	 *
@@ -117,6 +123,12 @@ typedef enum {
 	 **/
 	OHMD_EXTERNAL_SENSOR_FUSION           = 19,
 
+	/** float[4] (get): Universal shader distortion coefficients (PanoTools model <a,b,c,d>. */
+	OHMD_UNIVERSAL_DISTORTION_K           = 20,
+
+	/** float[3] (get): Universal shader aberration coefficients (post warp scaling <r,g,b>. */
+	OHMD_UNIVERSAL_ABERRATION_K           = 21,
+
 } ohmd_float_value;
 
 /** A collection of int value information types used for getting information with ohmd_device_geti(). */
@@ -226,6 +238,18 @@ OHMD_APIENTRYDLL void OHMD_APIENTRY ohmd_ctx_update(ohmd_context* ctx);
 OHMD_APIENTRYDLL int OHMD_APIENTRY ohmd_ctx_probe(ohmd_context* ctx);
 
 /**
+ * Get string from openhmd.
+ *
+ * Gets a string from OpenHMD. This is where non-device specific strings reside.
+ * This is where the distortion shader sources can be retrieved.
+ *
+ * @param type The name of the string to fetch. One of OHMD_GLSL_DISTORTION_FRAG_SRC, and OHMD_GLSL_DISTORTION_FRAG_SRC.
+ * @param out The location to return a const char*
+ * @return 0 on success, <0 on failure.
+ **/
+OHMD_APIENTRYDLL int ohmd_gets(ohmd_string_description type, const char** out);
+
+/**
  * Get device description from enumeration list index.
  *
  * Gets a human readable device description string from a zero indexed enumeration index

+ 24 - 9
src/drv_oculus_rift/rift.c

@@ -315,12 +315,29 @@ static ohmd_device* open_device(ohmd_driver* driver, ohmd_device_desc* desc)
 	priv->base.properties.lens_vpos = priv->display_info.v_center;
 	priv->base.properties.ratio = ((float)priv->display_info.h_resolution / (float)priv->display_info.v_resolution) / 2.0f;
 
+	//setup generic distortion coeffs, from hand-calibration
+	switch (desc->revision) {
+		case REV_DK2:
+			ohmd_set_universal_distortion_k(&(priv->base.properties), 0.247, -0.145, 0.103, 0.795);
+			ohmd_set_universal_aberration_k(&(priv->base.properties), 0.985, 1.000, 1.015);
+			break;
+		case REV_DK1:
+			ohmd_set_universal_distortion_k(&(priv->base.properties), 1.003, -1.005, 0.403, 0.599);
+			ohmd_set_universal_aberration_k(&(priv->base.properties), 0.985, 1.000, 1.015);
+			break;
+		case REV_CV1:
+			ohmd_set_universal_distortion_k(&(priv->base.properties), 0.098, .324, -0.241, 0.819);
+			ohmd_set_universal_aberration_k(&(priv->base.properties), 0.9952420, 1.0, 1.0008074);
+			/* CV1 reports IPD, but not lens center, at least not anywhere I could find, so use the manually measured value of 0.054 */
+			priv->display_info.lens_separation = 0.054;
+			priv->base.properties.lens_sep = priv->display_info.lens_separation;
+		default:
+			break;
+	}
+
 	// calculate projection eye projection matrices from the device properties
 	//ohmd_calc_default_proj_matrices(&priv->base.properties);
 	float l,r,t,b,n,f;
-	mat4x4f l_proj; // left projection matrix
-	mat4x4f r_proj; // right projection matrix
-	mat4x4f translate;
 	// left eye screen bounds
 	l = -1.0f * (priv->display_info.h_screen_size/2 - priv->display_info.lens_separation/2);
 	r = priv->display_info.lens_separation/2;
@@ -329,18 +346,16 @@ static ohmd_device* open_device(ohmd_driver* driver, ohmd_device_desc* desc)
 	n = priv->display_info.eye_to_screen_distance[0];
 	f = n*10e6;
 	//LOGD("l: %0.3f, r: %0.3f, b: %0.3f, t: %0.3f, n: %0.3f, f: %0.3f", l,r,b,t,n,f);
-	omat4x4f_init_frustum(&l_proj, l, r, b, t, n, f);
-	omat4x4f_init_translate(&translate, r, 0, 0); //shift over eye offset
-	omat4x4f_mult(&translate, &l_proj, &priv->base.properties.proj_left); //LEFT multiply inside OpenHMD
+	/* eye separation is handled by IPD in the Modelview matrix */
+	omat4x4f_init_frustum(&priv->base.properties.proj_left, l, r, b, t, n, f);
 	//right eye screen bounds
 	l = -1.0f * priv->display_info.lens_separation/2;
 	r = priv->display_info.h_screen_size/2 - priv->display_info.lens_separation/2;
 	n = priv->display_info.eye_to_screen_distance[1];
 	f = n*10e6;
 	//LOGD("l: %0.3f, r: %0.3f, b: %0.3f, t: %0.3f, n: %0.3f, f: %0.3f", l,r,b,t,n,f);
-	omat4x4f_init_frustum(&r_proj, l, r, b, t, n, f);
-	omat4x4f_init_translate(&translate, l, 0, 0); //shift over eye offset
-	omat4x4f_mult(&translate, &r_proj, &priv->base.properties.proj_right); //LEFT multiply inside OpenHMD
+	/* eye separation is handled by IPD in the Modelview matrix */
+	omat4x4f_init_frustum(&priv->base.properties.proj_right, l, r, b, t, n, f);
 
 	priv->base.properties.fov = 2 * atan2f(
 			priv->display_info.h_screen_size/2 - priv->display_info.lens_separation/2,

+ 44 - 1
src/openhmd.c

@@ -8,6 +8,7 @@
 /* Main Lib Implemenation */
 
 #include "openhmdi.h"
+#include "shaders.h"
 #include <stdlib.h>
 #include <string.h>
 #include <stdio.h>
@@ -97,6 +98,20 @@ int OHMD_APIENTRY ohmd_ctx_probe(ohmd_context* ctx)
 	return ctx->list.num_devices;
 }
 
+int OHMD_APIENTRY ohmd_gets(ohmd_string_description type, const char ** out)
+{
+	switch(type){
+	case OHMD_GLSL_DISTORTION_VERT_SRC:
+		*out = distortion_vert;
+		return OHMD_S_OK;
+	case OHMD_GLSL_DISTORTION_FRAG_SRC:
+		*out = distortion_frag;
+		return OHMD_S_OK;
+	default:
+		return OHMD_S_UNSUPPORTED;
+	}
+}
+
 const char* OHMD_APIENTRY ohmd_list_gets(ohmd_context* ctx, int index, ohmd_string_value type)
 {
 	if(index >= ctx->list.num_devices)
@@ -303,7 +318,18 @@ static int ohmd_device_getf_unp(ohmd_device* device, ohmd_float_value type, floa
 
 		return OHMD_S_OK;
 	}
-
+	case OHMD_UNIVERSAL_DISTORTION_K: {
+		for (int i = 0; i < 4; i++) {
+			out[i] = device->properties.universal_distortion_k[i];
+		}
+		return OHMD_S_OK;
+	}
+	case OHMD_UNIVERSAL_ABERRATION_K: {
+		for (int i = 0; i < 3; i++) {
+			out[i] = device->properties.universal_aberration_k[i];
+		}
+		return OHMD_S_OK;
+	}
 	default:
 		return device->getf(device, type, out);
 	}
@@ -491,6 +517,8 @@ void ohmd_set_default_device_properties(ohmd_device_properties* props)
 	props->ipd = 0.061f;
 	props->znear = 0.1f;
 	props->zfar = 1000.0f;
+	ohmd_set_universal_distortion_k(props, 0, 0, 0, 1);
+	ohmd_set_universal_aberration_k(props, 1.0, 1.0, 1.0);
 }
 
 void ohmd_calc_default_proj_matrices(ohmd_device_properties* props)
@@ -521,3 +549,18 @@ void ohmd_calc_default_proj_matrices(ohmd_device_properties* props)
 	omat4x4f_init_translate(&translate, -proj_offset, 0, 0);
 	omat4x4f_mult(&translate, &proj_base, &props->proj_right);
 }
+
+void ohmd_set_universal_distortion_k(ohmd_device_properties* props, float a, float b, float c, float d)
+{
+	props->universal_distortion_k[0] = a;
+	props->universal_distortion_k[1] = b;
+	props->universal_distortion_k[2] = c;
+	props->universal_distortion_k[3] = d;
+}
+
+void ohmd_set_universal_aberration_k(ohmd_device_properties* props, float r, float g, float b)
+{
+	props->universal_aberration_k[0] = r;
+	props->universal_aberration_k[1] = g;
+	props->universal_aberration_k[2] = b;
+}

+ 4 - 0
src/openhmdi.h

@@ -77,6 +77,8 @@ typedef struct {
 
 		mat4x4f proj_left; // adjusted projection matrix for left screen
 		mat4x4f proj_right; // adjusted projection matrix for right screen
+		float universal_distortion_k[4]; //PanoTools lens distiorion model [a,b,c,d]
+		float universal_aberration_k[3]; //post-warp per channel scaling [r,g,b]
 } ohmd_device_properties;
 
 struct ohmd_device_settings
@@ -131,6 +133,8 @@ struct ohmd_context {
 // helper functions
 void ohmd_set_default_device_properties(ohmd_device_properties* props);
 void ohmd_calc_default_proj_matrices(ohmd_device_properties* props);
+void ohmd_set_universal_distortion_k(ohmd_device_properties* props, float a, float b, float c, float d);
+void ohmd_set_universal_aberration_k(ohmd_device_properties* props, float r, float g, float b);
 
 // drivers
 ohmd_driver* ohmd_create_dummy_drv(ohmd_context* ctx);

+ 55 - 0
src/shaders.c

@@ -0,0 +1,55 @@
+const char distortion_vert[] =
+"#version 120\n"
+"void main(void)\n"
+"{\n"
+	"gl_TexCoord[0] = gl_MultiTexCoord0;\n"
+    "gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;\n"
+"}";
+
+const char distortion_frag[] =
+"#version 120\n"
+"\n"
+"//per eye texture to warp for lens distortion\n"
+"uniform sampler2D warpTexture;\n"
+"\n"
+"//Position of lens center in m (usually eye_w/2, eye_h/2)\n"
+"uniform vec2 LensCenter;\n"
+"//Scale from texture co-ords to m (usually eye_w, eye_h)\n"
+"uniform vec2 ViewportScale;\n"
+"//Distortion overall scale in m (usually ~eye_w/2)\n"
+"uniform float WarpScale;\n"
+"//Distoriton coefficients (PanoTools model) [a,b,c,d]\n"
+"uniform vec4 HmdWarpParam;\n"
+"\n"
+"//chromatic distortion post scaling\n"
+"uniform vec3 aberr;\n"
+"\n"
+"void main()\n"
+"{\n"
+    "//output_loc is the fragment location on screen from [0,1]x[0,1]\n"
+    "vec2 output_loc = vec2(gl_TexCoord[0].s, gl_TexCoord[0].t);\n"
+    "//Compute fragment location in lens-centered co-ordinates at world scale\n"
+	"vec2 r = output_loc * ViewportScale - LensCenter;\n"
+    "//scale for distortion model\n"
+    "//distortion model has r=1 being the largest circle inscribed (e.g. eye_w/2)\n"
+    "r /= WarpScale;\n"
+"\n"
+    "//|r|**2\n"
+    "float r_mag = length(r);\n"
+    "//offset for which fragment is sourced\n"
+    "vec2 r_displaced = r * (HmdWarpParam.w + HmdWarpParam.z * r_mag +\n"
+		"HmdWarpParam.y * r_mag * r_mag +\n"
+		"HmdWarpParam.x * r_mag * r_mag * r_mag);\n"
+    "//back to world scale\n"
+    "r_displaced *= WarpScale;\n"
+    "//back to viewport co-ord\n"
+    "vec2 tc_r = (LensCenter + aberr.r * r_displaced) / ViewportScale;\n"
+    "vec2 tc_g = (LensCenter + aberr.g * r_displaced) / ViewportScale;\n"
+    "vec2 tc_b = (LensCenter + aberr.b * r_displaced) / ViewportScale;\n"
+"\n"
+    "float red = texture2D(warpTexture, tc_r).r;\n"
+    "float green = texture2D(warpTexture, tc_g).g;\n"
+    "float blue = texture2D(warpTexture, tc_b).b;\n"
+    "//Black edges off the texture\n"
+    "gl_FragColor = ((tc_g.x < 0.0) || (tc_g.x > 1.0) || (tc_g.y < 0.0) || (tc_g.y > 1.0)) ? vec4(0.0, 0.0, 0.0, 1.0) : vec4(red, green, blue, 1.0);\n"
+"}";

+ 6 - 0
src/shaders.h

@@ -0,0 +1,6 @@
+#ifndef SHADERS_H
+#define SHADERS_H
+
+const char * const distortion_vert;
+const char * const distortion_frag;
+#endif /* SHADERS_H */