ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

das.glsl (12137B)


      1 /* See LICENSE for license details. */
      2 layout(std430, binding = 1) readonly restrict buffer buffer_1 {
      3 	vec2 rf_data[];
      4 };
      5 
      6 #if DAS_FAST
      7 layout(rg32f, binding = 0)           restrict uniform image3D  u_out_data_tex;
      8 #else
      9 layout(rg32f, binding = 0) writeonly restrict uniform image3D  u_out_data_tex;
     10 #endif
     11 
     12 layout(r16i,  binding = 1) readonly  restrict uniform iimage1D sparse_elements;
     13 layout(rg32f, binding = 2) readonly  restrict uniform image1D  focal_vectors;
     14 
     15 #define C_SPLINE 0.5
     16 
     17 #define TX_ROWS 0
     18 #define TX_COLS 1
     19 
     20 #define TX_MODE_TX_COLS(a) (((a) & 2) != 0)
     21 #define TX_MODE_RX_COLS(a) (((a) & 1) != 0)
     22 
     23 vec2 rotate_iq(vec2 iq, float time)
     24 {
     25 	vec2 result = iq;
     26 	if (center_frequency > 0) {
     27 		float arg    = radians(360) * center_frequency * time;
     28 		mat2  phasor = mat2( cos(arg), sin(arg),
     29 		                    -sin(arg), cos(arg));
     30 		result = phasor * iq;
     31 	}
     32 	return result;
     33 }
     34 
     35 /* NOTE: See: https://cubic.org/docs/hermite.htm */
     36 vec2 cubic(int base_index, float index)
     37 {
     38 	mat4 h = mat4(
     39 		 2, -3,  0, 1,
     40 		-2,  3,  0, 0,
     41 		 1, -2,  1, 0,
     42 		 1, -1,  0, 0
     43 	);
     44 
     45 	float tk, t = modf(index, tk);
     46 	vec2 samples[4] = {
     47 		rf_data[base_index + int(tk) - 1],
     48 		rf_data[base_index + int(tk) + 0],
     49 		rf_data[base_index + int(tk) + 1],
     50 		rf_data[base_index + int(tk) + 2],
     51 	};
     52 
     53 	vec4 S  = vec4(t * t * t, t * t, t, 1);
     54 	vec2 P1 = samples[1];
     55 	vec2 P2 = samples[2];
     56 	vec2 T1 = C_SPLINE * (P2 - samples[0]);
     57 	vec2 T2 = C_SPLINE * (samples[3] - P1);
     58 
     59 	mat2x4 C = mat2x4(vec4(P1.x, P2.x, T1.x, T2.x), vec4(P1.y, P2.y, T1.y, T2.y));
     60 	vec2 result = S * h * C;
     61 	return result;
     62 }
     63 
     64 vec2 sample_rf(int channel, int transmit, float index)
     65 {
     66 	vec2 result     = vec2(index >= 0.0f) * vec2((int(index) + 1 + int(interpolate)) < dec_data_dim.x);
     67 	int  base_index = channel * dec_data_dim.x * dec_data_dim.z + transmit * dec_data_dim.x;
     68 	if (interpolate) result *= cubic(base_index, index);
     69 	else             result *= rf_data[base_index + int(round(index))];
     70 	result = rotate_iq(result, index / sampling_frequency);
     71 	return result;
     72 }
     73 
     74 float sample_index(float distance)
     75 {
     76 	float  time = distance / speed_of_sound + time_offset;
     77 	return time * sampling_frequency;
     78 }
     79 
     80 float apodize(float arg)
     81 {
     82 	/* NOTE: used for constant F# dynamic receive apodization. This is implemented as:
     83 	 *
     84 	 *                  /        |x_e - x_i|\
     85 	 *    a(x, z) = cos(F# * π * ----------- ) ^ 2
     86 	 *                  \        |z_e - z_i|/
     87 	 *
     88 	 * where x,z_e are transducer element positions and x,z_i are image positions. */
     89 	float a = cos(clamp(abs(arg), 0, 0.25 * radians(360)));
     90 	return a * a;
     91 }
     92 
     93 vec2 rca_plane_projection(vec3 point, bool rows)
     94 {
     95 	vec2 result = vec2(point[int(rows)], point[2]);
     96 	return result;
     97 }
     98 
     99 float plane_wave_transmit_distance(vec3 point, float transmit_angle, bool tx_rows)
    100 {
    101 	return dot(rca_plane_projection(point, tx_rows), vec2(sin(transmit_angle), cos(transmit_angle)));
    102 }
    103 
    104 float cylindrical_wave_transmit_distance(vec3 point, float focal_depth, float transmit_angle, bool tx_rows)
    105 {
    106 	vec2 f = focal_depth * vec2(sin(transmit_angle), cos(transmit_angle));
    107 	return distance(rca_plane_projection(point, tx_rows), f);
    108 }
    109 
    110 #if DAS_FAST
    111 vec3 RCA(vec3 world_point)
    112 {
    113 	bool  tx_rows         = !TX_MODE_TX_COLS(transmit_mode);
    114 	bool  rx_rows         = !TX_MODE_RX_COLS(transmit_mode);
    115 	vec2  xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows);
    116 	vec2  focal_vector    = imageLoad(focal_vectors, u_channel).xy;
    117 	float transmit_angle  = radians(focal_vector.x);
    118 	float focal_depth     = focal_vector.y;
    119 
    120 	float transmit_distance;
    121 	if (isinf(focal_depth)) {
    122 		transmit_distance = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    123 	} else {
    124 		transmit_distance = cylindrical_wave_transmit_distance(world_point, focal_depth,
    125 		                                                       transmit_angle, tx_rows);
    126 	}
    127 
    128 	vec2 result = vec2(0);
    129 	for (int channel = 0; channel < dec_data_dim.y; channel++) {
    130 		vec2  receive_vector   = xdc_world_point - rca_plane_projection(vec3(channel * xdc_element_pitch, 0), rx_rows);
    131 		float receive_distance = length(receive_vector);
    132 		float apodization      = apodize(f_number * radians(180) / abs(xdc_world_point.y) * receive_vector.x);
    133 
    134 		if (apodization > 0) {
    135 			float sidx  = sample_index(transmit_distance + receive_distance);
    136 			result     += apodization * sample_rf(channel, u_channel, sidx);
    137 		}
    138 	}
    139 	return vec3(result, 0);
    140 }
    141 #else
    142 vec3 RCA(vec3 world_point)
    143 {
    144 	bool tx_rows         = !TX_MODE_TX_COLS(transmit_mode);
    145 	bool rx_rows         = !TX_MODE_RX_COLS(transmit_mode);
    146 	vec2 xdc_world_point = rca_plane_projection((xdc_transform * vec4(world_point, 1)).xyz, rx_rows);
    147 
    148 	vec3 sum = vec3(0);
    149 	for (int transmit = 0; transmit < dec_data_dim.z; transmit++) {
    150 		vec2  focal_vector   = imageLoad(focal_vectors, transmit).xy;
    151 		float transmit_angle = radians(focal_vector.x);
    152 		float focal_depth    = focal_vector.y;
    153 
    154 		float transmit_distance;
    155 		if (isinf(focal_depth)) {
    156 			transmit_distance = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    157 		} else {
    158 			transmit_distance = cylindrical_wave_transmit_distance(world_point, focal_depth,
    159 			                                                       transmit_angle, tx_rows);
    160 		}
    161 
    162 		for (int rx_channel = 0; rx_channel < dec_data_dim.y; rx_channel++) {
    163 			vec3  rx_center      = vec3(rx_channel * xdc_element_pitch, 0);
    164 			vec2  receive_vector = xdc_world_point - rca_plane_projection(rx_center, rx_rows);
    165 			float apodization    = apodize(f_number * radians(180) / abs(xdc_world_point.y) * receive_vector.x);
    166 
    167 			if (apodization > 0) {
    168 				float sidx   = sample_index(transmit_distance + length(receive_vector));
    169 				vec2  value  = apodization * sample_rf(rx_channel, transmit, sidx);
    170 				sum         += vec3(value, length(value));
    171 			}
    172 		}
    173 	}
    174 	return sum;
    175 }
    176 #endif
    177 
    178 #if DAS_FAST
    179 vec3 HERCULES(vec3 world_point)
    180 {
    181 	bool  uhercules       = das_shader_id == DAS_ID_UHERCULES;
    182 	vec3  xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
    183 	bool  tx_rows         = !TX_MODE_TX_COLS(transmit_mode);
    184 	bool  rx_cols         =  TX_MODE_RX_COLS(transmit_mode);
    185 	vec2  focal_vector    = imageLoad(focal_vectors, 0).xy;
    186 	float transmit_angle  = radians(focal_vector.x);
    187 	float focal_depth     = focal_vector.y;
    188 
    189 	float transmit_distance;
    190 	if (isinf(focal_depth)) {
    191 		transmit_distance = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    192 	} else {
    193 		transmit_distance = cylindrical_wave_transmit_distance(world_point, focal_depth,
    194 		                                                       transmit_angle, tx_rows);
    195 	}
    196 
    197 	vec2 result = vec2(0);
    198 	for (int transmit = int(uhercules); transmit < dec_data_dim.z; transmit++) {
    199 		int tx_channel = uhercules ? imageLoad(sparse_elements, transmit - int(uhercules)).x : transmit;
    200 		vec3 element_position;
    201 		if (rx_cols) element_position = vec3(u_channel,  tx_channel, 0) * vec3(xdc_element_pitch, 0);
    202 		else         element_position = vec3(tx_channel, u_channel,  0) * vec3(xdc_element_pitch, 0);
    203 
    204 		float apodization = apodize(f_number * radians(180) / abs(xdc_world_point.z) *
    205 		                            distance(xdc_world_point.xy, element_position.xy));
    206 		if (apodization > 0) {
    207 			/* NOTE: tribal knowledge */
    208 			if (transmit == 0) apodization *= inversesqrt(dec_data_dim.z);
    209 
    210 			float sidx  = sample_index(transmit_distance + distance(xdc_world_point, element_position));
    211 			result     += apodization * sample_rf(u_channel, transmit, sidx);
    212 		}
    213 	}
    214 	return vec3(result, 0);
    215 }
    216 #else
    217 vec3 HERCULES(vec3 world_point)
    218 {
    219 	bool  uhercules       = das_shader_id == DAS_ID_UHERCULES;
    220 	vec3  xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
    221 	bool  tx_rows         = !TX_MODE_TX_COLS(transmit_mode);
    222 	bool  rx_cols         =  TX_MODE_RX_COLS(transmit_mode);
    223 	vec2  focal_vector    = imageLoad(focal_vectors, 0).xy;
    224 	float transmit_angle  = radians(focal_vector.x);
    225 	float focal_depth     = focal_vector.y;
    226 
    227 	float transmit_distance;
    228 	if (isinf(focal_depth)) {
    229 		transmit_distance = plane_wave_transmit_distance(world_point, transmit_angle, tx_rows);
    230 	} else {
    231 		transmit_distance = cylindrical_wave_transmit_distance(world_point, focal_depth,
    232 		                                                       transmit_angle, tx_rows);
    233 	}
    234 
    235 	vec3 result = vec3(0);
    236 	for (int transmit = int(uhercules); transmit < dec_data_dim.z; transmit++) {
    237 		int tx_channel = uhercules ? imageLoad(sparse_elements, transmit - int(uhercules)).x : transmit;
    238 		for (int rx_channel = 0; rx_channel < dec_data_dim.y; rx_channel++) {
    239 			vec3 element_position;
    240 			if (rx_cols) element_position = vec3(rx_channel, tx_channel, 0) * vec3(xdc_element_pitch, 0);
    241 			else         element_position = vec3(tx_channel, rx_channel, 0) * vec3(xdc_element_pitch, 0);
    242 
    243 			float apodization = apodize(f_number * radians(180) / abs(xdc_world_point.z) *
    244 			                            distance(xdc_world_point.xy, element_position.xy));
    245 			if (apodization > 0) {
    246 				/* NOTE: tribal knowledge */
    247 				if (transmit == 0) apodization *= inversesqrt(dec_data_dim.z);
    248 
    249 				float sidx   = sample_index(transmit_distance + distance(xdc_world_point, element_position));
    250 				vec2  value  = apodization * sample_rf(rx_channel, transmit, sidx);
    251 				result      += vec3(value, length(value));
    252 			}
    253 		}
    254 	}
    255 	return result;
    256 }
    257 #endif
    258 
    259 #if DAS_FAST
    260 vec3 FORCES(vec3 world_point)
    261 {
    262 	bool  uforces          = das_shader_id == DAS_ID_UFORCES;
    263 	vec3  xdc_world_point  = (xdc_transform * vec4(world_point, 1)).xyz;
    264 	float receive_distance = distance(xdc_world_point.xz, vec2(u_channel * xdc_element_pitch.x, 0));
    265 	float apodization      = apodize(f_number * radians(180) / abs(xdc_world_point.z) *
    266 	                                 (xdc_world_point.x - u_channel * xdc_element_pitch.x));
    267 
    268 	vec2 result = vec2(0);
    269 	if (apodization > 0) {
    270 		for (int transmit = int(uforces); transmit < dec_data_dim.z; transmit++) {
    271 			int   tx_channel      = uforces ? imageLoad(sparse_elements, transmit - int(uforces)).x : transmit;
    272 			vec3  transmit_center = vec3(xdc_element_pitch * vec2(tx_channel, floor(dec_data_dim.y / 2)), 0);
    273 
    274 			float sidx  = sample_index(distance(xdc_world_point, transmit_center) + receive_distance);
    275 			result     += apodization * sample_rf(u_channel, transmit, sidx);
    276 		}
    277 	}
    278 	return vec3(result, 0);
    279 }
    280 #else
    281 vec3 FORCES(vec3 world_point)
    282 {
    283 	bool uforces         = das_shader_id == DAS_ID_UFORCES;
    284 	vec3 xdc_world_point = (xdc_transform * vec4(world_point, 1)).xyz;
    285 
    286 	vec3 result = vec3(0);
    287 	for (int rx_channel = 0; rx_channel < dec_data_dim.y; rx_channel++) {
    288 		float receive_distance = distance(xdc_world_point.xz, vec2(rx_channel * xdc_element_pitch.x, 0));
    289 		float apodization      = apodize(f_number * radians(180) / abs(xdc_world_point.z) *
    290 		                                 (xdc_world_point.x - rx_channel * xdc_element_pitch.x));
    291 		if (apodization > 0) {
    292 			for (int transmit = int(uforces); transmit < dec_data_dim.z; transmit++) {
    293 				int   tx_channel      = uforces ? imageLoad(sparse_elements, transmit - int(uforces)).x : transmit;
    294 				vec3  transmit_center = vec3(xdc_element_pitch * vec2(tx_channel, floor(dec_data_dim.y / 2)), 0);
    295 
    296 				float sidx   = sample_index(distance(xdc_world_point, transmit_center) + receive_distance);
    297 				vec2  value  = apodization * sample_rf(rx_channel, tx_channel, sidx);
    298 				result      += vec3(value, length(value));
    299 			}
    300 		}
    301 	}
    302 	return result;
    303 }
    304 #endif
    305 
    306 void main()
    307 {
    308 	ivec3 out_voxel = ivec3(gl_GlobalInvocationID);
    309 #if DAS_FAST
    310 	vec3 sum = vec3(imageLoad(u_out_data_tex, out_voxel).xy, 0);
    311 #else
    312 	vec3 sum = vec3(0);
    313 	out_voxel += u_voxel_offset;
    314 #endif
    315 
    316 	vec3 world_point = (u_voxel_transform * vec4(out_voxel, 1)).xyz;
    317 
    318 	switch (das_shader_id) {
    319 	case DAS_ID_FORCES:
    320 	case DAS_ID_UFORCES:
    321 	{
    322 		sum += FORCES(world_point);
    323 	}break;
    324 	case DAS_ID_HERCULES:
    325 	case DAS_ID_UHERCULES:
    326 	{
    327 		sum += HERCULES(world_point);
    328 	}break;
    329 	case DAS_ID_FLASH:
    330 	case DAS_ID_RCA_TPW:
    331 	case DAS_ID_RCA_VLS:
    332 	{
    333 		sum += RCA(world_point);
    334 	}break;
    335 	}
    336 
    337 	/* TODO(rnp): scale such that brightness remains ~constant */
    338 	if (coherency_weighting) sum.xy *= sum.xy / (sum.z + float(sum.z == 0));
    339 
    340 	imageStore(u_out_data_tex, out_voxel, vec4(sum.xy, 0, 0));
    341 }