Im trying to update the Unity PhotoCpature example to be usable within a function and not just running on start. However when I try and take the Start() code out and place it in another function Snap() the code no longer works and I am greeted with errors.
Errors Recieved
I do not understand how the why these errors continue to pop up when nothing has changed except for where the function is being called from.
Here is my code
using UnityEngine;
using System.Collections;
using System.Linq;
using UnityEngine.XR.WSA.WebCam;
public class TakePic : MonoBehaviour
{
PhotoCapture photoCaptureObject = null;
Texture2D targetTexture = null;
void Start()
{
Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
// Create a PhotoCapture object
PhotoCapture.CreateAsync(false, delegate (PhotoCapture captureObject)
{
photoCaptureObject = captureObject;
CameraParameters cameraParameters = new CameraParameters();
cameraParameters.hologramOpacity = 0.0f;
cameraParameters.cameraResolutionWidth = cameraResolution.width;
cameraParameters.cameraResolutionHeight = cameraResolution.height;
cameraParameters.pixelFormat = CapturePixelFormat.BGRA32;
// Activate the camera
photoCaptureObject.StartPhotoModeAsync(cameraParameters, delegate (PhotoCapture.PhotoCaptureResult result)
{
// Take a picture
photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
});
});
}
public void Snap()
{
Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
// Create a PhotoCapture object
PhotoCapture.CreateAsync(false, delegate (PhotoCapture captureObject)
{
photoCaptureObject = captureObject;
CameraParameters cameraParameters = new CameraParameters();
cameraParameters.hologramOpacity = 0.0f;
cameraParameters.cameraResolutionWidth = cameraResolution.width;
cameraParameters.cameraResolutionHeight = cameraResolution.height;
cameraParameters.pixelFormat = CapturePixelFormat.BGRA32;
// Activate the camera
photoCaptureObject.StartPhotoModeAsync(cameraParameters, delegate (PhotoCapture.PhotoCaptureResult result)
{
// Take a picture
photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
});
});
}
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
// Copy the raw image data into our target texture
photoCaptureFrame.UploadImageDataToTexture(targetTexture);
// Create a gameobject that we can apply our texture to
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
Renderer quadRenderer = quad.GetComponent<Renderer>() as Renderer;
quadRenderer.material = new Material(Shader.Find("Unlit/Texture"));
quad.transform.parent = this.transform;
quad.transform.localPosition = new Vector3(0.0f, 0.0f, 3.0f);
quadRenderer.material.SetTexture("_MainTex", targetTexture);
// Deactivate our camera
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
// Shutdown our photo capture resource
Debug.Log("ShutDown!");
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
}
Related
I'm following this tutorial to take a picture using the Hololens 2 camera and show it using a billboard (Quad object). The code I'm using is the following:
using UnityEngine;
using System.Collections;
using System.Linq;
using UnityEngine.Windows.WebCam;
public class PhotoCaptureExample : MonoBehaviour
{
PhotoCapture photoCaptureObject = null;
Texture2D targetTexture = null;
// Use this for initialization
void Start()
{
Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
// Create a PhotoCapture object
PhotoCapture.CreateAsync(false, delegate(PhotoCapture captureObject) {
photoCaptureObject = captureObject;
CameraParameters cameraParameters = new CameraParameters();
cameraParameters.hologramOpacity = 0.0f;
cameraParameters.cameraResolutionWidth = cameraResolution.width;
cameraParameters.cameraResolutionHeight = cameraResolution.height;
cameraParameters.pixelFormat = CapturePixelFormat.BGRA32;
// Activate the camera
photoCaptureObject.StartPhotoModeAsync(cameraParameters, delegate(PhotoCapture.PhotoCaptureResult result) {
// Take a picture
photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
});
});
}
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
// Copy the raw image data into our target texture
photoCaptureFrame.UploadImageDataToTexture(targetTexture);
// Create a gameobject that we can apply our texture to
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
Renderer quadRenderer = quad.GetComponent<Renderer>() as Renderer;
quadRenderer.material = new Material(Shader.Find("Unlit/Texture"));
quad.transform.parent = this.transform;
quad.transform.localPosition = new Vector3(0.0f, 0.0f, 3.0f);
quadRenderer.material.SetTexture("_MainTex", targetTexture);
// Deactivate our camera
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
// Shutdown our photo capture resource
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
}
I've added some log messages that I can see on the scene as well when I deploy the app. I've managed to get the picture and save it in a file in other script, so I know for a fact that the code is working. However, the Quad object added to the scene is blank.
I'm running this in an empty scene which runs the code shown above. The GameObject1 is a GameObject with the script attached, and debugText is my TextMeshPro element to show debugging messages.
I've tried this tutorial as well but with the same result. I'm using Unity 2019.4.26f1 (I cannot change it because of the project requirements) and Visual Studio 2019.
I'm writing a unity editor script which draws a preview scene in the inspector GUI. Basically, I instantiate a prefab with a camera component and move it into a temporary scene. Then I try to draw the scene onto a texture using that camera. My current approach doesn't seem to be working, or maybe there's something wrong in my code. I'd appreciate any help.
Below is some of my code that does the drawing:
[CustomEditor(typeof(NPCSpawnConfig))]
public class NPCSpawnEditor : Editor
{
enum SupportedAspects
{
Aspect4by3 = 1,
Aspect5by4 = 2,
Aspect16by10 = 3,
Aspect16by9 = 4
};
Camera _cam = null;
RenderTexture _rt;
Texture2D _tex2d;
Scene _scene;
// preview variables
SupportedAspects _aspectChoiceIdx = SupportedAspects.Aspect16by10;
float _curAspect;
// world space (orthographicSize)
float _worldScreenHeight = 5;
int _renderTextureHeight = 1080;
float ToFloat(SupportedAspects aspects)
{
switch(aspects)
{
case SupportedAspects.Aspect16by10:
return 16 / 10f;
case SupportedAspects.Aspect16by9:
return 16 / 9f;
case SupportedAspects.Aspect4by3:
return 4 / 3f;
case SupportedAspects.Aspect5by4:
return 5 / 4f;
default:
throw new ArgumentException();
}
}
void DrawRefScene()
{
_rt = new RenderTexture(Mathf.RoundToInt(_curAspect * _renderTextureHeight), _renderTextureHeight, 16);
_cam.targetTexture = _rt;
_cam.Render();
_tex2d = new Texture2D(_rt.width, _rt.height, TextureFormat.RGBA32, false);
_tex2d.Apply(false);
Graphics.CopyTexture(_rt, _tex2d);
}
Vector2 GetGUIPreviewSize()
{
Vector2 camSizeWorld = new Vector2(_worldScreenHeight * _curAspect, _worldScreenHeight);
float scaleFactor = EditorGUIUtility.currentViewWidth / camSizeWorld.x;
return new Vector2(EditorGUIUtility.currentViewWidth, scaleFactor * camSizeWorld.y);
}
#region Init
void OnEnable()
{
void OpenSceneDelay()
{
EditorApplication.delayCall -= OpenSceneDelay;
DrawRefScene();
}
_aspectChoiceIdx = SupportedAspects.Aspect16by10;
_scene = EditorSceneManager.NewPreviewScene();
PrefabUtility.LoadPrefabContentsIntoPreviewScene("Assets/Prefabs/Demo/DemoBkg.prefab", _scene);
_cam = _scene.GetRootGameObjects()[0].GetComponentInChildren<Camera>();
_curAspect = ToFloat(_aspectChoiceIdx);
_cam.aspect = _curAspect;
_cam.orthographicSize = _worldScreenHeight;
EditorApplication.delayCall += OpenSceneDelay;
}
void OnDisable()
{
EditorSceneManager.ClosePreviewScene(_scene);
}
#endregion
void OnCamSettingChange()
{
_curAspect = ToFloat(_aspectChoiceIdx);
_cam.aspect = _curAspect;
_cam.orthographicSize = _worldScreenHeight;
DrawRefScene();
}
// GUI states
class GUIControlStates
{
public bool foldout = false;
};
GUIControlStates _guiStates = new GUIControlStates();
public override void OnInspectorGUI()
{
// draw serializedObject fields
// ....
// display options
using (var scope = new EditorGUI.ChangeCheckScope())
{
_aspectChoiceIdx = (SupportedAspects)EditorGUILayout.EnumPopup("label", (Enum)_aspectChoiceIdx);
if (scope.changed)
{
OnCamSettingChange();
}
}
_guiStates.foldout = EditorGUILayout.Foldout(_guiStates.foldout, "label", true);
if(_guiStates.foldout)
{
using (var scope = new EditorGUI.ChangeCheckScope())
{
_worldScreenHeight = EditorGUILayout.FloatField("label", _worldScreenHeight);
_renderTextureHeight = EditorGUILayout.IntField("label", _renderTextureHeight);
if (scope.changed)
{
OnCamSettingChange();
}
}
}
if (_tex2d != null)
{
Vector2 sz = GetGUIPreviewSize();
Rect r = EditorGUILayout.GetControlRect(false,
GUILayout.Height(sz.y),
GUILayout.ExpandHeight(false));
EditorGUI.DrawPreviewTexture(r, _tex2d);
}
}
}
Here is the result: (only clear color is displayed, but the prefab contains a lot of sprites that should be drawn. The camera is also correctly positioned relative to the sprites.)
Solved this by adding the following 2 lines after getting the camera component.
_cam.cameraType = CameraType.Preview;
_cam.scene = _scene;
I work with Unity3D (2020.3.26f1), MRTK and Hololens2.
In my case I have a unity scene that contains a button. If the user clicks the button, an image should be taken via the HoloLens 2 webcam and displayed in the scene. I used the code from the Unity Documentation (this works better for me than the microsoft documentation).
If I deploy the code below to the HoloLens 2, start the app and click the button that is supposed to take the picture, nothing happens. I added the script correctly.
For debugging reasons, I then connected my webcam to the computer and started the Unity scene. Now when I click the button in the game window, the console shows me the following error:
ArgumentNullException: Value cannot be null.Parameter name: shader
The error is referring to line 44:
quadRenderer.material = new Material(Shader.Find("Custom/Unlit/UnlitTexture"));
I can't figgure out the problem maybe someone can help here.
This is the code im Using:
using UnityEngine;
using System.Collections;
using System;
using System.Linq;
using UnityEngine.Assertions;
using UnityEngine.Windows.WebCam;
public class PhotoCaptureExample : MonoBehaviour
{
PhotoCapture photoCaptureObject = null;
Texture2D targetTexture = null;
// Use this for initialization
public void TakePhoto()
{
Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
// Create a PhotoCapture object
PhotoCapture.CreateAsync(false, delegate (PhotoCapture captureObject) {
photoCaptureObject = captureObject;
CameraParameters cameraParameters = new CameraParameters();
cameraParameters.hologramOpacity = 0.0f;
cameraParameters.cameraResolutionWidth = cameraResolution.width;
cameraParameters.cameraResolutionHeight = cameraResolution.height;
cameraParameters.pixelFormat = CapturePixelFormat.BGRA32;
// Activate the camera
photoCaptureObject.StartPhotoModeAsync(cameraParameters, delegate (PhotoCapture.PhotoCaptureResult result) {
// Take a picture
photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
});
});
}
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
// Copy the raw image data into the target texture
photoCaptureFrame.UploadImageDataToTexture(targetTexture);
// Create a GameObject to which the texture can be applied
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
Renderer quadRenderer = quad.GetComponent<Renderer>() as Renderer;
quadRenderer.material = new Material(Shader.Find("Custom/Unlit/UnlitTexture"));
quad.transform.parent = this.transform;
quad.transform.localPosition = new Vector3(0.0f, 0.0f, 3.0f);
quadRenderer.material.SetTexture("_MainTex", targetTexture);
// Deactivate the camera
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
// Shutdown the photo capture resource
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
}
I am trying to capture an image using HoloLens. I accept the permission to use the HoloLens camera.
I am using this code
using UnityEngine;
using System.Collections;
using System.Linq;
using UnityEngine.XR.WSA.WebCam;
using UnityEngine.UI;
public class WebCamera : MonoBehaviour
{
PhotoCapture photoCaptureObject = null;
Texture2D targetTexture = null;
// Use this for initialization
void Start()
{
Resolution cameraResolution = PhotoCapture.SupportedResolutions.OrderByDescending((res) => res.width * res.height).First();
targetTexture = new Texture2D(cameraResolution.width, cameraResolution.height);
// Create a PhotoCapture object
PhotoCapture.CreateAsync(false, delegate (PhotoCapture captureObject) {
photoCaptureObject = captureObject;
CameraParameters cameraParameters = new CameraParameters();
cameraParameters.hologramOpacity = 0.0f;
cameraParameters.cameraResolutionWidth = cameraResolution.width;
cameraParameters.cameraResolutionHeight = cameraResolution.height;
cameraParameters.pixelFormat = CapturePixelFormat.BGRA32;
// Activate the camera
photoCaptureObject.StartPhotoModeAsync(cameraParameters, delegate (PhotoCapture.PhotoCaptureResult result) {
// Take a picture
photoCaptureObject.TakePhotoAsync(OnCapturedPhotoToMemory);
});
});
}
void OnCapturedPhotoToMemory(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
// Copy the raw image data into the target texture
photoCaptureFrame.UploadImageDataToTexture(targetTexture);
// Create a GameObject to which the texture can be applied
GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
Renderer quadRenderer = quad.GetComponent<Renderer>() as Renderer;
quadRenderer.material = new Material(Shader.Find("Custom/Unlit/UnlitTexture"));
quad.transform.parent = this.transform;
quad.transform.localPosition = new Vector3(0.0f, 0.0f, 3.0f);
quadRenderer.material.SetTexture("_MainTex", targetTexture);
// Deactivate the camera
photoCaptureObject.StopPhotoModeAsync(OnStoppedPhotoMode);
}
void OnStoppedPhotoMode(PhotoCapture.PhotoCaptureResult result)
{
// Shutdown the photo capture resource
photoCaptureObject.Dispose();
photoCaptureObject = null;
}
}
This script was attached to an empty game object.
I am getting the following error
ArgumentNullException: Value cannot be null
Parameter name: shader.UnityEngine.Material..ctor(UnityEngine.Shader shader) (at <00000000000000000000>:0)
Please can anyone help to capture an image. Where did I go wrong?
It's looking for a shader located here, "Custom/Unlit/UnlitTexture". Which may be unavailable.
I'd suggest commenting out that line to determine if the other code around it works.
You have to make 2 changes:
in Unity Editor, Edit > Project Settings > Graphics > to the list "Always included shaders", Add "Unlit/Texture"
Update the line in the code to Shader.Find("Unlit/Texture");
Every 5 seconds I need to take a photo and display it in the world. That part is working fine.
Once a photo is taken it need to disappear 1 second after. That's where I'm stuck.
I've gotten it to disappear but then I cant get the new picture to reappear when its function is called again.
Any ideas?
I've tried:
m_CanvasRenderer.enabled = false;
m_CanvasRenderer.enabled = true;
m_Canvas = null;
m_Canvas.setActive(false);
m_Canvas.setActive(true);
With no luck
public class PhotoCaptureExample : MonoBehaviour
{
GestureRecognizer m_GestureRecognizer;
GameObject m_Canvas = null;
Renderer m_CanvasRenderer = null;
PhotoCapture m_PhotoCaptureObj;
CameraParameters m_CameraParameters;
bool m_CapturingPhoto = false;
Texture2D m_Texture = null;
void Start()
{
Initialize();
InvokeRepeating("TakePhoto", 5.0f, 5.0f);
//StartCoroutine(TakePhoto());
}
void TakePhoto()
{
if (m_CapturingPhoto)
{
return;
}
m_CapturingPhoto = true;
m_PhotoCaptureObj.TakePhotoAsync(OnPhotoCaptured);
}
void OnPhotoCaptured(PhotoCapture.PhotoCaptureResult result, PhotoCaptureFrame photoCaptureFrame)
{
// m_CanvasRenderer.enabled = true;
if (m_Canvas == null)
{
m_Canvas = GameObject.CreatePrimitive(PrimitiveType.Quad);
m_Canvas.name = "PhotoCaptureCanvas";
m_CanvasRenderer = m_Canvas.GetComponent<Renderer>() as Renderer;
m_CanvasRenderer.material = new Material(Shader.Find("AR/HolographicImageBlend"));
}
Matrix4x4 cameraToWorldMatrix;
photoCaptureFrame.TryGetCameraToWorldMatrix(out cameraToWorldMatrix);
Matrix4x4 worldToCameraMatrix = cameraToWorldMatrix.inverse;
Matrix4x4 projectionMatrix;
photoCaptureFrame.TryGetProjectionMatrix(out projectionMatrix);
photoCaptureFrame.UploadImageDataToTexture(m_Texture);
m_Texture.wrapMode = TextureWrapMode.Clamp;
m_CanvasRenderer.sharedMaterial.SetTexture("_MainTex", m_Texture);
m_CanvasRenderer.sharedMaterial.SetMatrix("_WorldToCameraMatrix", worldToCameraMatrix);
m_CanvasRenderer.sharedMaterial.SetMatrix("_CameraProjectionMatrix", projectionMatrix);
m_CanvasRenderer.sharedMaterial.SetFloat("_VignetteScale", 1.0f);
// Position the canvas object slightly in front
// of the real world web camera.
Vector3 position = cameraToWorldMatrix.GetColumn(3) - cameraToWorldMatrix.GetColumn(2);
// Rotate the canvas object so that it faces the user.
Quaternion rotation = Quaternion.LookRotation(-cameraToWorldMatrix.GetColumn(2), cameraToWorldMatrix.GetColumn(1));
m_Canvas.transform.position = position;
m_Canvas.transform.rotation = rotation;
m_CapturingPhoto = false;
float counter = 0; float target = 1;
while (counter < target)
{
counter += Time.deltaTime;
}
// m_CanvasRenderer.enabled = false;
}
}