问题
I'm really new with unity3D and I would like to ask a question I have a 3D human model ( a default unity model) which has a hierarchical bone structure.
What I want to achieve here is, when I press certain trigger, I want to color one of its limb with different color (just one of its limb). This is the illustration of what I want to achieve
I'm really clueless about this, because I've just started learning Unity for about 3 months ago, so I really need your help, this is the property of my renderer if it's helping
回答1:
I just wanted to add that there may be an easier way to achieve what you want using projectors. This is the common tool in unity used to draw various effects on mesh surface in real time, such as bullet holes. Using the same principle you can highlight some area of the mesh. You can think of a projector as flashlight, that everything its light hits changes its texture. There are some example projectors under Standard Assets / Effects. You might want to start there.
To create a projectror, create an empty game object => Add Component => Projector.
EDIT
Another idea you may want to try is to use vertex colors. Each vertex of a mesh contains, in addition to coordinates, a color parameter, which is accessible via shader. So, you can change the color of specific set of vertices in order to highlight them. There are 2 points here you have to pay attention to:
1) Most shaders choose to ignore vertex color, with exception of sprites shader. You will need a custom shader, such as this one.
2) You need to know somehow which vertices exactly you want to highlight. What you can do is to iterate over Mesh.boneWeights. If you want to select an arm, for example, what you need is all vertices that have weight > 1 for arm bone index. How do you find the arm bone index? This index corresponds to the index in SkinnedMeshRenderer.bones. Or just pick some vertex you know for sure belongs to arm and see its bone weights to find the index.
Here a sample script that changes vertex colors based on selected bone index. Attach it to your SkinnedMeshRenderer (usually a second level hierarchy object):
using UnityEngine;
using System.Collections;
public class BoneHiglighter : MonoBehaviour {
public Color32 highlightColor = Color.red;
public Color32 regularColor = Color.white;
public SkinnedMeshRenderer smr;
// Just for sake of demonstration
public Transform bone;
private Transform prevBone;
// Find bone index given bone transform
int GetBoneIndex(Transform bone) {
Debug.Assert(smr != null);
var bones = smr.bones;
for (int i = 0; i < bones.Length; ++i) {
if (bones[i] == bone) return i;
}
return -1;
}
// Change vertex colors highlighting given bone
void Highlight(Transform bone) {
Debug.Assert(smr != null);
var idx = GetBoneIndex(bone);
var mesh = smr.sharedMesh;
var weights = mesh.boneWeights;
var colors = new Color32[weights.Length];
for (int i = 0; i < colors.Length; ++i) {
float sum = 0;
if (weights[i].boneIndex0 == idx && weights[i].weight0 > 0)
sum += weights[i].weight0;
if (weights[i].boneIndex1 == idx && weights[i].weight1 > 0)
sum += weights[i].weight1;
if (weights[i].boneIndex2 == idx && weights[i].weight2 > 0)
sum += weights[i].weight2;
if (weights[i].boneIndex3 == idx && weights[i].weight3 > 0)
sum += weights[i].weight3;
colors[i] = Color32.Lerp(regularColor, highlightColor, sum);
}
mesh.colors32 = colors;
}
void Start() {
// If not explicitly specified SkinnedMeshRenderer try to find one
if (smr == null) smr = GetComponent<SkinnedMeshRenderer>();
// SkinnedMeshRenderer has only shared mesh. We should not modify it.
// So we make a copy on startup, and work with it.
smr.sharedMesh = (Mesh)Instantiate(smr.sharedMesh);
Highlight(bone);
}
void Update() {
if (prevBone != bone) {
// User selected different bone
prevBone = bone;
Highlight(bone);
}
}
}
See a sample project: https://www.dropbox.com/s/yfoqo44bubcr48s/HighlightBone.zip?dl=0
Demo: https://dl.dropboxusercontent.com/u/16950335/bones/index.html - click on body parts to see them highlight.
回答2:
You'll have to do some reading if you are a beginner. Before changing something make sure you fully understand the model setup:
- This worker has 1 single mesh (3d model).
- This model is rendered by 1 material (Skinned mesh renderer has only 1 material attached)
- This material has a base RGBA texture which colors your model. Now here is a catch: to map 2D texture on a 3D model we use UV mapping. UV mapping is a kind of bridge that links each vertex on a 3D model to a 2D coordinate on the plane. In 3D we call them a Vertex, on 2D we call - UV. So when you color the area around one UV on 2D plane (in photoshop, gimp, etc.), the appropriate vertex will get colored on the mesh. Google UV mapping for more info. NOTE: you can not do UV mapping in Unity (unless via script), it is usually done in an external 3D modeling software.
- Finally, after having done all the googling you can switch to your task. You probably have understood by now that you need to modify the texture of your model. You would need to find where does your model's hand get mapped to, then change the color of that area with a script. You do it with Texture2D.GetPixels() and SetPixels().
There also is another solution for this, less programmatic, more logical, but crude way that needs optimization:
- Import that model into a 3D modeling software
- Detach the arm but make sure it is still controlled with the skeleton
- Import the model back into Unity
- Make a new material for the arm, you can duplicate the existing one (it's a common practice to make separate materials for objects that change visual properties)
- Duplicate your RGBA texture and desaturate it in an image editing software.
- Assign this duplicate texture to the duplicate material's color slot, and the duplicate material to the arm model.
- The arm should be grayscale by now so you can change its color by changing the material color.
来源:https://stackoverflow.com/questions/34460587/unity-changing-only-certain-part-of-3d-models-color