Updated Mesh Colliders of all Humans and added FOV Representation
This commit is contained in:
@@ -24,11 +24,13 @@ public class EyeTrackingCollisionPoints : MonoBehaviour
|
||||
if ( hitPoint != null && Physics.Raycast(transform.position, rayCastDirection, out hit, rayDistance, layersToInclude))
|
||||
{
|
||||
hitPoint.transform.position = new Vector3(hit.point.x, hit.point.y, hit.point.z);
|
||||
|
||||
GameObject hitObject = hit.collider.gameObject;
|
||||
|
||||
if (hit.collider.GetType() == typeof(MeshCollider))
|
||||
{
|
||||
hit.collider.gameObject.GetComponent<QuadScript>().addHitPoint(hit.textureCoord.x, hit.textureCoord.y);
|
||||
hitObject.GetComponent<QuadScript>().addHitPoint(hit.textureCoord.x, hit.textureCoord.y);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,3 +40,4 @@ public class EyeTrackingCollisionPoints : MonoBehaviour
|
||||
return hitPoint.transform;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,144 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
public class FieldOfView : MonoBehaviour
|
||||
{
|
||||
public float viewRadius;
|
||||
|
||||
[Range(0,360)]
|
||||
public float periAngle;
|
||||
|
||||
[Range(0,180)]
|
||||
public float foveaAngle;
|
||||
|
||||
public Transform eyeLeft;
|
||||
public Transform eyeRight;
|
||||
|
||||
public LayerMask targetMask;
|
||||
public LayerMask obstacleMask;
|
||||
|
||||
public float meshResolution;
|
||||
public MeshFilter periMeshFilter;
|
||||
public MeshFilter foveaMeshFilter;
|
||||
private Mesh periMesh;
|
||||
private Mesh foveaMesh;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
periMesh = new Mesh();
|
||||
periMesh.name = "View Mesh";
|
||||
periMeshFilter.mesh = periMesh;
|
||||
|
||||
foveaMesh = new Mesh();
|
||||
foveaMesh.name = "View Mesh";
|
||||
foveaMeshFilter.mesh = foveaMesh;
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
Vector3 gazeDirection = ((eyeLeft.forward + eyeRight.forward) * 0.5f).normalized;
|
||||
float gazeAngle = AngleFromDir(gazeDirection, true);
|
||||
DrawFOV(transform.eulerAngles.y, periMesh, periAngle);
|
||||
DrawFOV(gazeAngle, foveaMesh, foveaAngle);
|
||||
}
|
||||
|
||||
public Vector3 DirFromAngle(float angleDeg, bool angleIsGlobal)
|
||||
{
|
||||
if (!angleIsGlobal)
|
||||
{
|
||||
angleDeg += transform.eulerAngles.y;
|
||||
}
|
||||
return new Vector3(Mathf.Sin(angleDeg * Mathf.Deg2Rad), 0, Mathf.Cos(angleDeg * Mathf.Deg2Rad));
|
||||
}
|
||||
|
||||
public float AngleFromDir(Vector3 dir, bool angleIsGlobal)
|
||||
{
|
||||
// Sicherheitsnetz: y‑Komponente ignorieren und Vektor normalisieren
|
||||
dir.y = 0;
|
||||
if (dir.sqrMagnitude < 0.0001f) return 0f;
|
||||
|
||||
// Atan2: (x, z) → Winkel in Rad, dann nach ° umrechnen
|
||||
float angleDeg = Mathf.Atan2(dir.x, dir.z) * Mathf.Rad2Deg;
|
||||
|
||||
// Bereich schön sauber auf 0–360 bringen
|
||||
angleDeg = (angleDeg + 360f) % 360f;
|
||||
|
||||
// Soll der Winkel relativ zum Objekt sein?
|
||||
if (!angleIsGlobal)
|
||||
angleDeg -= transform.eulerAngles.y;
|
||||
|
||||
// Und noch einmal normalisieren, damit auch hier 0–360 garantiert ist
|
||||
return (angleDeg + 360f) % 360f;
|
||||
}
|
||||
|
||||
void DrawFOV(float startAngle, Mesh viewMesh, float viewAngle)
|
||||
{
|
||||
int stepCount = Mathf.RoundToInt(foveaAngle * meshResolution);
|
||||
float stepAngleSize = viewAngle / stepCount;
|
||||
List<Vector3> viewPoints = new List<Vector3>();
|
||||
|
||||
for (int i = 0; i <= stepCount; i++)
|
||||
{
|
||||
float angle = startAngle - viewAngle / 2 + stepAngleSize * i;
|
||||
|
||||
Debug.DrawLine(transform.position, transform.position + DirFromAngle(angle, true) * viewRadius, Color.red);
|
||||
|
||||
ViewCastInfo newViewCast = ViewCast(angle);
|
||||
viewPoints.Add(newViewCast.hitPoint);
|
||||
}
|
||||
|
||||
int vertexcount = viewPoints.Count + 1;
|
||||
Vector3[] vertices = new Vector3[vertexcount];
|
||||
int[] triangles = new int[(vertexcount-2) * 3];
|
||||
|
||||
vertices[0] = Vector3.zero;
|
||||
|
||||
for (int i = 0; i < vertexcount-1; i++)
|
||||
{
|
||||
vertices[i+1] = transform.InverseTransformPoint(viewPoints[i]);
|
||||
|
||||
if (i < vertexcount - 2)
|
||||
{
|
||||
triangles[i * 3] = 0;
|
||||
triangles[i * 3 + 1] = i + 1;
|
||||
triangles[i * 3 + 2] = i + 2;
|
||||
}
|
||||
|
||||
}
|
||||
viewMesh.Clear();
|
||||
viewMesh.vertices = vertices;
|
||||
viewMesh.triangles = triangles;
|
||||
viewMesh.RecalculateNormals();
|
||||
}
|
||||
|
||||
ViewCastInfo ViewCast(float globalAngle)
|
||||
{
|
||||
Vector3 direction = DirFromAngle(globalAngle, true);
|
||||
RaycastHit hit;
|
||||
|
||||
if (Physics.Raycast(transform.position, direction, out hit, viewRadius, obstacleMask))
|
||||
{
|
||||
return new ViewCastInfo(true, hit.point, hit.distance, globalAngle);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new ViewCastInfo(false, transform.position + direction * viewRadius, viewRadius, globalAngle);
|
||||
}
|
||||
}
|
||||
|
||||
public struct ViewCastInfo
|
||||
{
|
||||
public bool didHit;
|
||||
public Vector3 hitPoint;
|
||||
public float distance;
|
||||
public float angle;
|
||||
|
||||
public ViewCastInfo(bool _hit, Vector3 _hitPoint, float _distance, float _angle)
|
||||
{
|
||||
didHit = _hit;
|
||||
hitPoint = _hitPoint;
|
||||
distance = _distance;
|
||||
angle = _angle;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 71c3238de06f215479526905859c78f0
|
||||
@@ -0,0 +1,25 @@
|
||||
using UnityEngine;
|
||||
|
||||
public class GazeDirection : MonoBehaviour
|
||||
{
|
||||
[Header("Head and Eye-Transforms")]
|
||||
public Transform headcam;
|
||||
public Transform eyeLeft;
|
||||
public Transform eyeRight;
|
||||
|
||||
[Header("Renderer Feature")]
|
||||
public GazeOverlayRendererFeature overlayRendererFeature;
|
||||
|
||||
GazeOverlayRendererFeature.GazeRenderPass pass;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
pass = overlayRendererFeature.GetPass();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
pass.gazeOrigin = (eyeLeft.position + eyeRight.position) * 0.5f; // Mittelpunkt zwischen den Augen
|
||||
pass.gazeDirection = ((eyeLeft.forward + eyeRight.forward) * 0.5f).normalized; // Errechnete mittlere Blickrichtung
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 318f8ae78f0375c4db5613429c9ef197
|
||||
@@ -0,0 +1,84 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
using UnityEngine.Rendering.RenderGraphModule;
|
||||
|
||||
// Render Feature, das den Sichtkegel mit fovealem und peripherem Bereich darstellt und pro Kamera aktiviert werden kann.
|
||||
public class GazeOverlayRendererFeature : ScriptableRendererFeature
|
||||
{
|
||||
public sealed class GazeRenderPass : ScriptableRenderPass
|
||||
{
|
||||
internal Vector3 gazeOrigin;
|
||||
internal Vector3 gazeDirection;
|
||||
internal float foveaHalfRad = Mathf.Deg2Rad * 2.5f; // für 5° fovealer Bereich
|
||||
internal float periHalfRad = Mathf.Deg2Rad * 10f; // für 20° peripheren Bereich
|
||||
|
||||
static readonly int gazeOriginID = Shader.PropertyToID("_GazeOrigin");
|
||||
static readonly int gazeDirectionID = Shader.PropertyToID("_GazeDirection");
|
||||
static readonly int foveaHalfRadID = Shader.PropertyToID("_FoveaAngle");
|
||||
static readonly int periHalfRadID = Shader.PropertyToID("_PeriAngle");
|
||||
|
||||
readonly Material gazeMaterial;
|
||||
|
||||
public GazeRenderPass(Material material) => gazeMaterial = material;
|
||||
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
||||
{
|
||||
Debug.Log("RecordRenderGraph");
|
||||
var resourceData = frameData.Get<UniversalResourceData>();
|
||||
TextureHandle depth = resourceData.activeDepthTexture;
|
||||
TextureHandle color = resourceData.activeColorTexture;
|
||||
|
||||
using var builder = renderGraph.AddRasterRenderPass<PassData>("GazeOverlay", out var passData);
|
||||
|
||||
passData.depth = depth;
|
||||
passData.color = color;
|
||||
passData.gazeMaterial = gazeMaterial;
|
||||
passData.gazeOrigin = gazeOrigin;
|
||||
passData.gazeDirection = gazeDirection;
|
||||
passData.foveaHalfRad = foveaHalfRad;
|
||||
passData.periHalfRad = periHalfRad;
|
||||
|
||||
builder.UseTexture(depth, AccessFlags.Read);
|
||||
builder.UseTexture(color, AccessFlags.ReadWrite);
|
||||
builder.SetRenderAttachment(color, 0);
|
||||
|
||||
builder.SetRenderFunc((PassData data, RasterGraphContext ctx) =>
|
||||
{
|
||||
ctx.cmd.ClearRenderTarget(RTClearFlags.Color, new Color(1, 0, 1, 1), 1.0f, 0);
|
||||
//ctx.cmd.SetGlobalVector(gazeOriginID, data.gazeOrigin);
|
||||
//ctx.cmd.SetGlobalVector(gazeDirectionID, data.gazeDirection);
|
||||
//ctx.cmd.SetGlobalFloat(foveaHalfRadID, data.foveaHalfRad);
|
||||
//ctx.cmd.SetGlobalFloat(periHalfRadID, data.periHalfRad);
|
||||
|
||||
//Blitter.BlitTexture(ctx.cmd, data.depth, new Vector4(1, 1, 0, 0),
|
||||
//data.gazeMaterial, 0);
|
||||
});
|
||||
}
|
||||
|
||||
class PassData
|
||||
{
|
||||
internal TextureHandle depth;
|
||||
internal TextureHandle color;
|
||||
internal Material gazeMaterial;
|
||||
internal Vector3 gazeOrigin, gazeDirection;
|
||||
internal float foveaHalfRad, periHalfRad;
|
||||
}
|
||||
}
|
||||
|
||||
[SerializeField] Material gazeOverlayMaterial;
|
||||
GazeRenderPass pass;
|
||||
|
||||
public GazeRenderPass GetPass() { return pass; }
|
||||
|
||||
public override void Create()
|
||||
{
|
||||
pass = new GazeRenderPass(gazeOverlayMaterial);
|
||||
Debug.Log("Feature created");
|
||||
}
|
||||
|
||||
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
||||
{
|
||||
renderer.EnqueuePass(pass);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0d63223eecd1d7e4dbb01821c5a37dbd
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 44a39c1f66022ab4e9d01650f2c52d08
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,40 @@
|
||||
Shader "Hidden/GazeOverlay"
|
||||
{
|
||||
SubShader
|
||||
{
|
||||
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
|
||||
pass
|
||||
{
|
||||
Name "GazeOverlayPass"
|
||||
ZTest Always
|
||||
ZWrite Off
|
||||
Blend SrcAlpha OneMinusSrcAlpha
|
||||
|
||||
HLSLPROGRAM
|
||||
#pragma vertex VertFullScreenTriangle
|
||||
#pragma fragment frag
|
||||
|
||||
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
|
||||
struct Varyings
|
||||
{
|
||||
float4 positionHCS : SV_POSITION;
|
||||
float2 texcoord : TEXCOORD0;
|
||||
};
|
||||
|
||||
Varyings VertFullScreenTriangle (uint vertexID : SV_VERTEXID)
|
||||
{
|
||||
Varyings o;
|
||||
o.positionHCS = GetFullScreenTriangleVertexPosition(vertexID);
|
||||
o.texcoord = GetFullScreenTriangleTexCoord(vertexID);
|
||||
return o;
|
||||
}
|
||||
|
||||
#include "GazeOverlayPass.hlsl"
|
||||
|
||||
ENDHLSL
|
||||
}
|
||||
}
|
||||
FallBack Off
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 777cec444743bc44b817fad6d93d915b
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
|
||||
|
||||
TEXTURE2D_X(_CameraDepthTexture);
|
||||
SAMPLER(sampler_CameraDepthTexture);
|
||||
|
||||
float3 _GazeOrigin;
|
||||
float3 _GazeDirection;
|
||||
float _FoveaAngle;
|
||||
float _PeriAngle;
|
||||
|
||||
float4 _FoveaColor = float4(0,1,0,0.25);
|
||||
float4 _periColor = float4(1,0,0,0.18);
|
||||
float4 _BlockedCol = float4(0.3,0.3,0.3,0.22);
|
||||
|
||||
half4 frag (Varyings input) : SV_Target
|
||||
{
|
||||
return float4(1, 0, 1, 0.5);
|
||||
// Nimmt die Depth Texture und rekonstruiert daraus die World Position
|
||||
//float depth = SAMPLE_TEXTURE2D_X(_CameraDepthTexture, sampler_CameraDepthTexture, input.texcoord).r;
|
||||
//float4 clip = float4(input.texcoord * 2 - 1, depth, 1);
|
||||
//float4 view = mul(UNITY_MATRIX_I_P, clip); view /= view.w;
|
||||
//float3 world = mul(UNITY_MATRIX_I_V, view).xyz;
|
||||
|
||||
// Eryeugt einen Vektor vom Origin zum gewünschten Pixel
|
||||
//float3 v = normalize(world - _GazeOrigin);
|
||||
//float cosAng = dot(v, normalize(_GazeOrigin));
|
||||
|
||||
// Berechnung Cosinus
|
||||
//float cosF = cos(_FoveaAngle);
|
||||
//float cosP = cos(_PeriAngle);
|
||||
|
||||
//float4 col;
|
||||
//if (cosAng >= cosF) col = _FoveaColor;
|
||||
//else if (cosAng >= cosP) col = _periColor;
|
||||
//else discard;
|
||||
|
||||
//uint gLayer = 8;
|
||||
//if (unity_RenderingLayer.x & (1u << gLayer))
|
||||
//col = _BlockedCol;
|
||||
|
||||
//return col;
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da1b9d6b6b4c65941be255287d846932
|
||||
ShaderIncludeImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user