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;
}
}
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 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");
I'm doing this tutorial https://youtu.be/T6bd_MQ2ass it is for iPhone tho.
I have followed the tutorial step by step and I can't figure out what seems to be the problem here.
Gyro of the phone is fine I tested for it (Moto G5) I don't have a webcam on my PC so can't say how it looks in the camera maybe that's why the image is so stretched out. I'm not too concerned about that just yet. if you could help me with that too it would be great.
This is the code for the main camera
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
public class webCamScript : MonoBehaviour {
public GameObject webCameraPlane;
public Button fireButton;
// Use this for initialization
void Start () {
if (Application.isMobilePlatform) {
GameObject cameraParent = new GameObject ("camParent");
cameraParent.transform.position = this.transform.position;
this.transform.parent = cameraParent.transform;
cameraParent.transform.Rotate (Vector3.right, 90);
}
Input.gyro.enabled = false;
Input.gyro.enabled = true;
fireButton.onClick.AddListener (OnButtonDown);
WebCamTexture webCameraTexture = new WebCamTexture();
webCameraPlane.GetComponent<MeshRenderer>().material.mainTexture = webCameraTexture;
webCameraTexture.Play();
}
void OnButtonDown(){
GameObject bullet = Instantiate(Resources.Load("bullet", typeof(GameObject))) as GameObject;
Rigidbody rb = bullet.GetComponent<Rigidbody>();
bullet.transform.rotation = Camera.main.transform.rotation;
bullet.transform.position = Camera.main.transform.position;
rb.AddForce(Camera.main.transform.forward * 1000f);
Destroy (bullet, 3);
GetComponent<AudioSource> ().Play ();
}
// Update is called once per frame
void Update () {
Quaternion cameraRotation = new Quaternion (-Input.gyro.attitude.x, -Input.gyro.attitude.y, -Input.gyro.attitude.z, -Input.gyro.attitude.w);
this.transform.localRotation = cameraRotation;
}
}
I'm doing this tutorial https://youtu.be/T6bd_MQ2ass it is for iPhone tho.
I have followed the tutorial step by step and I can't figure out what seems to be the problem here.
This is how it should look like
This is how it is looking (there is no gyro control here.)
In my Unity project I would like to use the FastObjImporter class found on the internet to put obj on the scene. Do I have to create an empty GameObject and assign processed obj to it?
Try with an empty game object:
GameObject go = new GameObject("obj");
myMesh = FastObjImporter.Instance.ImportFile(objPath);
Instantiate(myMesh, Vector3.zero, Quaternion.identity);
go.AddComponent<MeshRenderer>();
go.GetComponent<MeshFilter>().mesh = myMesh;
Earlier I tried this way but it also did not work:
GameObject go;
myMesh = FastObjImporter.Instance.ImportFile(objPath);
Instantiate(myMesh, Vector3.zero, Quaternion.identity);
go.GetComponent<MeshFilter>().mesh = myMesh;
Could someone explain what I'm doing wrong?
CODE:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using System.IO;
/// <summary>
/// Sample
/// </summary>
public class DriveTest : MonoBehaviour
{
//public static readonly string REQUIRED_MIME = "image/jpeg";
//public string filePath;
public string objPath;
bool initInProgress = false;
GoogleDrive drive;
public string url;
List<GoogleDrive.File> remoteObjFiles;
public Mesh myMesh;
public GameObject go;
/* ---------------------------------------------------------------------------------------------------------------------------------- */
void Start()
{
objPath = Application.persistentDataPath + "/testObj.obj";
StartCoroutine(ImportObj());
go = new GameObject("obj");
}
/* ---------------------------------------------------------------------------------------------------------------------------------- */
void Update()
{
if (Input.GetKey(KeyCode.Escape))
Application.Quit();
}
/* ---------------------------------------------------------------------------------------------------------------------------------- */
IEnumerator ImportObj()
{
initInProgress = true;
drive = new GoogleDrive();
var driveInit = drive.Initialize();
while (driveInit.MoveNext())
yield return null;
initInProgress = false;
for (;;)
{
( SEARCHING AND SETTING DOWNLOAD FOLDER IN GDRIVE )
var download = drive.DownloadFile(remoteObjFiles[0]);
yield return StartCoroutine(download);
var data = GoogleDrive.GetResult<byte[]>(download);
if (File.Exists(objPath))
{
File.Delete(objPath);
File.WriteAllBytes(objPath, data);
}
else
File.WriteAllBytes(objPath, data);
Debug.Log("Obj: " + remoteObjFiles[0].ToString());
if (File.Exists(objPath))
{
Debug.Log("Loading OBJ from the device");
myMesh = FastObjImporter.Instance.ImportFile(objPath);
Debug.Log("Obj imported...");
Instantiate(myMesh, Vector3.zero, Quaternion.identity);
go.AddComponent<MeshRenderer>();
go.GetComponent<MeshFilter>().mesh = myMesh;
}
break;
}
}
Regards
There is no need to instantiate the Mesh. It is a component and I am not sure if you can even instantiate it. Just assign it to the material.
The main problem in your code is that you are not creating a material and a shader. You need to create a material, then create a "Standard" shader and assign that shader to the that material.
I noticed that the FastObjImporter script is old and decided to try it to make sure it is functioning properly. I ran into an index exception bug in it then fixed it. You can grab the copy of the fixed version here. See line #57 for the actual fix I made.
Functions to create MeshFilder, Material and MeshRenderer:
void attachMeshFilter(GameObject target, Mesh mesh)
{
MeshFilter mF = target.AddComponent<MeshFilter>();
mF.mesh = mesh;
}
Material createMaterial()
{
Material mat = new Material(Shader.Find("Standard"));
return mat;
}
void attachMeshRenderer(GameObject target, Material mat)
{
MeshRenderer mR = target.AddComponent<MeshRenderer>();
mR.material = mat;
}
Function to create new GameObject, load the model and attach every necessary components to it in order to display it:
GameObject loadAndDisplayMesh(string path)
{
//Create new GameObject to hold it
GameObject meshHolder = new GameObject("Loaded Mesh");
//Load Mesh
Mesh mesh = FastObjImporter.Instance.ImportFile(path);
//return null;
//Attach Mesh Filter
attachMeshFilter(meshHolder, mesh);
//Create Material
Material mat = createMaterial();
//Attach Mesh Renderer
attachMeshRenderer(meshHolder, mat);
return meshHolder;
}
Usage:
void Start()
{
string objPath = Application.persistentDataPath + "/testObj.obj";
GameObject obj = loadAndDisplayMesh(objPath);
//Position it in front od=f the camera. Your ZOffset may be different
Camera cam = Camera.main;
float zOffset = 40f;
obj.transform.position = cam.ViewportToWorldPoint(new Vector3(0.5f, 0.5f, cam.nearClipPlane + zOffset));
}
EDIT:
In some rare cases, Unity cannot find the shader on Android when Shader.Find("Standard") is used. You get the error when that happens.
A work around for this would be to create a Material in the Editor and assign the "Standard" shader to it. It should use "Standard" shader by default.
1.Create a material and name is "StandardMat".
2.Put that material inside a folder called "Resources". Create it if it doesn't exist. You must spell this correctly. Again, the folder must be named "Resources" and placed inside the "Asset" folder.
3.You can load the material with Resources.Load("StandardMat") as Material.
In the createMaterial function above,
Change
Material mat = new Material(Shader.Find("Standard"));
to
Material mat = Resources.Load("StandardMat") as Material;
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;
}
}